From caa8e040476b6b3bc2df08ec6414dde32b618af1 Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Fri, 18 Sep 2026 16:56:29 +0200 Subject: [PATCH] style: limit line length to 79 characters Limited the lengths of all lines to 79 characters (if possible). Additionally, some other lines were breaked for consistence. --- src/jcloud_docsgen/cli/cli.py | 25 +- src/jcloud_docsgen/cli/languages/python.py | 18 +- src/jcloud_docsgen/core/python/_core.py | 255 +- src/jcloud_docsgen/core/python/arguments.py | 160 +- src/jcloud_docsgen/core/python/definitions.py | 35 +- src/jcloud_docsgen/core/python/namespaces.py | 46 +- src/jcloud_docsgen/exceptions.py | 40 +- src/jcloud_docsgen/utils.py | 51 +- tests/unit/cli/test_python.py | 4 +- ..._PythonDefinitionDocumentationGenerator.py | 2232 ++++++++++++++--- .../test_PythonDocumentationGenerator.py | 89 +- .../project/src/user_management/service.py | 8 +- ...test_PythonModuleDocumentationGenerator.py | 336 ++- .../python/_core/test__collect_definitions.py | 351 ++- .../_core/test__function_params_info.py | 19 +- .../core/python/_core/test__markdown_table.py | 435 +++- .../_core/test__signature_argument_list.py | 263 +- .../test_PythonASTArgumentsListParser.py | 1095 ++++++-- .../arguments/test_PythonFunctionArgument.py | 574 ++++- .../definitions/test_PythonClassDefinition.py | 522 +++- .../definitions/test_PythonDefinition.py | 457 +++- .../test_PythonFunctionDefinition.py | 474 +++- .../namespaces/test_PythonPackageNamespace.py | 265 +- .../utils/test_assert_that_is_instance.py | 36 +- tests/unit/utils/test_human_readable_list.py | 7 +- 25 files changed, 6726 insertions(+), 1071 deletions(-) diff --git a/src/jcloud_docsgen/cli/cli.py b/src/jcloud_docsgen/cli/cli.py index f079be0..126c2ba 100644 --- a/src/jcloud_docsgen/cli/cli.py +++ b/src/jcloud_docsgen/cli/cli.py @@ -35,7 +35,9 @@ def existing_directory(path: str) -> pathlib.Path: _path = path path = pathlib.Path(_path) if not path.exists(): - raise argparse.ArgumentTypeError(f'{_path}: no such file or directory') + raise argparse.ArgumentTypeError( + f'{_path}: no such file or directory' + ) if not path.is_dir(): raise argparse.ArgumentTypeError(f'{_path}: not a directory') return path @@ -79,7 +81,10 @@ def create_argument_parser() -> argparse.ArgumentParser: argument_parser = argparse.ArgumentParser() - subparsers = argument_parser.add_subparsers(dest = 'language', required = True) + subparsers = argument_parser.add_subparsers( + dest = 'language', + required = True + ) python_parser = subparsers.add_parser( _LANG_PYTHON, @@ -99,19 +104,29 @@ def create_argument_parser() -> argparse.ArgumentParser: nargs = '+', default = [], choices = VALID_PYTHON_SECTIONS, - help = 'the sections to be excluded in the documentation. If a section shall be included AND excluded, it is excluded.' + help = ( + 'the sections to be excluded in the documentation. ' + 'If a section shall be included AND excluded, it is excluded.' + ) ) python_parser.add_argument( '-H', '--allow-html', action = 'store_true', - help = 'indicates whether HTML is allowed in the documentation. By default, HTML is disabled.' + help = ( + 'indicates whether HTML is allowed in the documentation. ' + 'By default, HTML is disabled.' + ) ) python_parser.add_argument( '-T', '--allow-tables', action = 'store_true', - help = 'indicates whether Markdown tables are allowed in the documentation. By default, Markdown tables are disabled. Warning: Markdown tables are not CommonMark.' + help = ( + 'indicates whether Markdown tables are allowed in the ' + 'documentation. By default, Markdown tables are disabled. ' + 'Warning: Markdown tables are not CommonMark.' + ) ) return argument_parser diff --git a/src/jcloud_docsgen/cli/languages/python.py b/src/jcloud_docsgen/cli/languages/python.py index b905416..e5ed08e 100644 --- a/src/jcloud_docsgen/cli/languages/python.py +++ b/src/jcloud_docsgen/cli/languages/python.py @@ -13,7 +13,10 @@ # limitations under the License. import pathlib -from ...core.python import PythonDefinitionDocumentationIncludeSections, PythonDocumentationGenerator +from ...core.python import ( + PythonDefinitionDocumentationIncludeSections, + PythonDocumentationGenerator +) from typing import Optional __all__ = [ @@ -22,9 +25,14 @@ __all__ = [ 'generate' ] -VALID_SECTIONS = list(PythonDefinitionDocumentationIncludeSections()._args.keys()) +VALID_SECTIONS = list( + PythonDefinitionDocumentationIncludeSections()._args.keys() +) -def parse_sections_list(include_sections: Optional[list[str]] = None, exclude_sections: list[str] = []) -> PythonDefinitionDocumentationIncludeSections: +def parse_sections_list( + include_sections: Optional[list[str]] = None, + exclude_sections: list[str] = [] +) -> PythonDefinitionDocumentationIncludeSections: ''' Returns a Python definition documentation sections list from the sections list to be included and the sections list to be excluded. @@ -63,7 +71,9 @@ def _generate( project_directory: pathlib.Path, output_directory: pathlib.Path, *, - include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(), + include_sections: PythonDefinitionDocumentationIncludeSections = ( + PythonDefinitionDocumentationIncludeSections() + ), allow_html: bool = False, allow_tables: bool = False ) -> None: diff --git a/src/jcloud_docsgen/core/python/_core.py b/src/jcloud_docsgen/core/python/_core.py index a4db189..b4f64f2 100644 --- a/src/jcloud_docsgen/core/python/_core.py +++ b/src/jcloud_docsgen/core/python/_core.py @@ -16,11 +16,16 @@ from __future__ import annotations from ...utils import non_empty_str, get_relative_path_segments from ...exceptions import PythonArgumentStructureError, InvalidNamespaceError from .namespaces import PythonModuleNamespace, PythonPackageNamespace -from .definitions import PythonDefinition, PythonFunctionDefinition, PythonAsyncFunctionDefinition, PythonClassDefinition +from .definitions import ( + PythonAsyncFunctionDefinition, + PythonClassDefinition, + PythonDefinition, + PythonFunctionDefinition +) from .arguments import PythonArgumentKind, PythonFunctionArgument from .docstrings import PythonDocstring from collections.abc import Iterator -from typing import Union +from typing import TypeAlias, Union from dataclasses import dataclass from enum import Enum import docstring_parser @@ -53,7 +58,17 @@ def _collect_definitions(tree_or_node) -> Iterator[PythonDefinition]: if isinstance(node, ast.ClassDef): yield PythonClassDefinition.from_node(node) -def _group_argument_list(argument_list: list[PythonFunctionArgument]) -> tuple[list[PythonFunctionArgument], list[PythonFunctionArgument], Union[PythonFunctionArgument, None], list[PythonFunctionArgument], Union[PythonFunctionArgument, None]]: +_Arguments: TypeAlias = tuple[ + list[PythonFunctionArgument], + list[PythonFunctionArgument], + Union[PythonFunctionArgument, None], + list[PythonFunctionArgument], + Union[PythonFunctionArgument, None] +] + +def _group_argument_list( + argument_list: list[PythonFunctionArgument] +) -> _Arguments: ''' Groups the argument list into a list of positional-only arguments, a list of normal arguments, the * argument if there is one, a list of @@ -62,7 +77,7 @@ def _group_argument_list(argument_list: list[PythonFunctionArgument]) -> tuple[l :param argument_list: The argument list. :type argument_list: list[PythonFunctionArgument] - :rtype: tuple[list[PythonFunctionArgument], list[PythonFunctionArgument], Union[PythonFunctionArgument, None], list[PythonFunctionArgument], Union[PythonFunctionArgument, None]] + :rtype: _Arguments ''' if argument_list: @@ -70,7 +85,9 @@ def _group_argument_list(argument_list: list[PythonFunctionArgument]) -> tuple[l arg.kind == PythonArgumentKind.KWARGS for arg in argument_list ]): - raise PythonArgumentStructureError('arguments cannot follow var-keyword argument') + raise PythonArgumentStructureError( + 'arguments cannot follow var-keyword argument' + ) positional_only_args = [] normal_args = [] @@ -87,7 +104,10 @@ def _group_argument_list(argument_list: list[PythonFunctionArgument]) -> tuple[l if arg.kind == PythonArgumentKind.VARARG: if vararg is not None: - raise PythonArgumentStructureError('* argument may only appear once', definition = arg) + raise PythonArgumentStructureError( + '* argument may only appear once', + definition = arg + ) vararg = arg if arg.kind == PythonArgumentKind.KEYWORD_ONLY: @@ -96,9 +116,17 @@ def _group_argument_list(argument_list: list[PythonFunctionArgument]) -> tuple[l if arg.kind == PythonArgumentKind.KWARGS: kwargs = arg - return positional_only_args, normal_args, vararg, keyword_only_args, kwargs + return ( + positional_only_args, + normal_args, + vararg, + keyword_only_args, + kwargs + ) -def _signature_argument_list(argument_list: list[PythonFunctionArgument]) -> list[str]: +def _signature_argument_list( + argument_list: list[PythonFunctionArgument] +) -> list[str]: ''' Returns a list of the arguments as strings for a function signature. @@ -109,7 +137,13 @@ def _signature_argument_list(argument_list: list[PythonFunctionArgument]) -> lis :rtype: list[str] ''' - positional_only_args, normal_args, vararg, keyword_only_args, kwargs = _group_argument_list(argument_list) + ( + positional_only_args, + normal_args, + vararg, + keyword_only_args, + kwargs + ) = _group_argument_list(argument_list) args = [] @@ -157,7 +191,15 @@ def _fit_row_length(expected: int, row: tuple) -> tuple: 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: +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. @@ -188,18 +230,34 @@ def _markdown_table(header: tuple, body: list[tuple], *, allow_markdown_table: b header_len = len(header) if allow_markdown_table: - table = '| ' + ' | '.join(header) + ' |\n|' + ' --- |' * len(header) + '\n' + 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{"s" if header_len > 1 else ""}, got {len(row)}') + raise ValueError( + f'expected {header_len} value{ + "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', ' ') + 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) for col in row @@ -210,15 +268,36 @@ def _markdown_table(header: tuple, body: list[tuple], *, allow_markdown_table: b for row in body: if len(row) != header_len: if force_row_lengths: - raise ValueError(f'expected {header_len} value{"s" if header_len > 1 else ""}, got {len(row)}') + raise ValueError( + f'expected {header_len} value{ + "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' + 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() @@ -277,7 +356,10 @@ def _argument_kind_human_readable(argument_kind: PythonArgumentKind) -> str: return argument_kind.name.lower().replace('_', '-') -def _function_params_info(arg_list: list[PythonFunctionArgument], docstring_arg_list: list[docstring_parser.DocstringParam]) -> dict[str, _FunctionParamInfo]: +def _function_params_info( + arg_list: list[PythonFunctionArgument], + docstring_arg_list: list[docstring_parser.DocstringParam] +) -> dict[str, _FunctionParamInfo]: ''' Returns a dictionary with the parameter info. @@ -297,7 +379,10 @@ def _function_params_info(arg_list: list[PythonFunctionArgument], docstring_arg_ :rtype: dict[str, _FunctionParamInfo] ''' - arg_annotations = {arg.name: _FunctionParamInfo(arg.annotation, None) for arg in arg_list} + arg_annotations = { + arg.name: _FunctionParamInfo(arg.annotation, None) + for arg in arg_list + } for arg in docstring_arg_list: if arg.arg_name in arg_annotations.keys(): @@ -375,7 +460,10 @@ class PythonDefinitionDocumentationIncludeSections: ) + ')' def __eq__(self, value) -> str: - if not isinstance(value, PythonDefinitionDocumentationIncludeSections): + if not isinstance( + value, + PythonDefinitionDocumentationIncludeSections + ): return False return self._args == value._args @@ -409,7 +497,9 @@ class PythonDefinitionDocumentationGenerator: def __init__( self, definition: PythonDefinition, - include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(), + include_sections: PythonDefinitionDocumentationIncludeSections = ( + PythonDefinitionDocumentationIncludeSections() + ), level: int = 1, *, allow_html: bool = True, @@ -431,7 +521,9 @@ class PythonDefinitionDocumentationGenerator: @property def _docstring(self): if self._docstring_cache is None: - self._docstring_cache = self.definition.doc.parse() if _is_docstring(self.definition.doc) else _NoDocstring + self._docstring_cache = self.definition.doc.parse() \ + if _is_docstring(self.definition.doc) \ + else _NoDocstring return self._docstring_cache def generate_signature(self) -> str: @@ -446,9 +538,15 @@ class PythonDefinitionDocumentationGenerator: ''' if isinstance(self.definition, PythonClassDefinition): - raise TypeError('Cannot generate a signature of a class definition.') + raise TypeError( + 'Cannot generate a signature of a class definition.' + ) - signature = self.definition.name + '(' + ', '.join(_signature_argument_list(self.definition.args)) + ')' + signature = self.definition.name + '(' + ', '.join( + _signature_argument_list( + self.definition.args + ) + ) + ')' if self.definition.returns is not None: signature += ' -> ' + self.definition.returns @@ -463,7 +561,13 @@ class PythonDefinitionDocumentationGenerator: :rtype: str ''' - return self.level * '#' + '# ' + ('Function' if not self.is_method else 'Method') +' signature\n\n```python\n' + self.generate_signature() + '\n```' + definition_type = 'Function' if not self.is_method else 'Method' + heading = self.level * '#' + '# ' + (definition_type) +' signature' + return f'''{heading} + +```python +{self.generate_signature()} +```''' def _generate_docstring_section(self) -> str: ''' @@ -477,7 +581,11 @@ class PythonDefinitionDocumentationGenerator: if _is_docstring(self.definition.doc) or not self.skip_empty_sections: if self.allow_html: - md += '
Docstring' + '' + md += f'
Docstring' else: md += self.level * '#' + '# Docstring\n\n' @@ -506,19 +614,30 @@ class PythonDefinitionDocumentationGenerator: if self.definition.args or not self.skip_empty_sections: md += self.level * '#' + '# Parameters\n\n' - params_types = _function_params_info(self.definition.args, self._docstring.params) + params_types = _function_params_info( + self.definition.args, + self._docstring.params + ) if params_types: - md += _markdown_table(['Parameter', 'Type', 'Kind', 'Default value', 'Description'], [ - ( - '`' + arg.name + '`', - ('`' + params_types.get(arg.name).annotation + '`') if params_types.get(arg.name).annotation is not None else None, - _argument_kind_human_readable(arg.kind), - ('`' + arg.default + '`') if arg.default is not None else None, - non_empty_str(params_types.get(arg.name).description) - ) - for arg in self.definition.args - ], allow_markdown_table = self.allow_tables) + md += _markdown_table( + ['Parameter', 'Type', 'Kind', 'Default value', 'Description'], + [ + ( + '`' + arg.name + '`', + ('`' + params_types.get(arg.name).annotation + '`') + if params_types.get(arg.name).annotation is not None + else None, + _argument_kind_human_readable(arg.kind), + ('`' + arg.default + '`') + if arg.default is not None + else None, + non_empty_str(params_types.get(arg.name).description) + ) + for arg in self.definition.args + ], + allow_markdown_table = self.allow_tables + ) md += '\n\n' @@ -537,7 +656,11 @@ class PythonDefinitionDocumentationGenerator: md = '' - if self._docstring.returns or self.definition.returns or not self.skip_empty_sections: + if ( + self._docstring.returns + or self.definition.returns + or not self.skip_empty_sections + ): md += self.level * '#' + '# Returns\n\n' returns = False @@ -555,7 +678,10 @@ class PythonDefinitionDocumentationGenerator: md += 'Return type: `' + rtype + '`' if not self.skip_empty_sections and not returns: - md += 'This function does not provide any information about its return value.' + md += ( + 'This function does not provide any ' + 'information about its return value.' + ) return md.strip() @@ -574,7 +700,11 @@ class PythonDefinitionDocumentationGenerator: if self._docstring.raises: md += _markdown_table(('Exception', 'Description'), [ - (('`' + exception.type_name + '`') if exception.type_name is not None else None, non_empty_str(exception.description)) + ( + ('`' + exception.type_name + '`') + if exception.type_name is not None + else None, non_empty_str(exception.description) + ) for exception in self._docstring.raises ], allow_markdown_table = self.allow_tables) elif not self.skip_empty_sections: @@ -689,7 +819,9 @@ class PythonDefinitionDocumentationGenerator: md = self.level * '#' + ' ' if isinstance(self.definition, PythonAsyncFunctionDefinition): - md += 'Asynchronous ' + ('function' if not self.is_method else 'method') + md += 'Asynchronous ' + ( + 'function' if not self.is_method else 'method' + ) elif isinstance(self.definition, PythonFunctionDefinition): if self.is_method and self.definition.name == '__init__': md += 'Constructor (method' @@ -699,7 +831,12 @@ class PythonDefinitionDocumentationGenerator: md += 'Class' md += ' `' + self.definition.name + '`' - if isinstance(self.definition, PythonFunctionDefinition) and not isinstance(self.definition, PythonAsyncFunctionDefinition) and self.is_method and self.definition.name == '__init__': + if ( + isinstance(self.definition, PythonFunctionDefinition) + and not isinstance(self.definition, PythonAsyncFunctionDefinition) + and self.is_method + and self.definition.name == '__init__' + ): md += ')' md += '\n\n' @@ -756,14 +893,23 @@ class PythonModuleDocumentationGenerator: self, module_path: pathlib.Path, src_directory: Union[pathlib.Path, None] = None, - include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(), + include_sections: PythonDefinitionDocumentationIncludeSections = ( + PythonDefinitionDocumentationIncludeSections() + ), **kwargs ) -> None: if module_path.name.count('.') > 1: - raise InvalidNamespaceError('namespace identifier cannot contain more than one dot', namespace_identifier = module_path.name) + raise InvalidNamespaceError( + 'namespace identifier cannot contain more than one dot', + namespace_identifier = module_path.name + ) self.module_path = pathlib.Path(str(module_path)) - self.src_directory = pathlib.Path(str(src_directory)) if src_directory is not None else None + self.src_directory = ( + pathlib.Path(str(src_directory)) + if src_directory is not None + else None + ) self.include_sections = include_sections self.kwargs = kwargs @@ -807,7 +953,12 @@ class PythonModuleDocumentationGenerator: md = '# Module `' if self.src_directory is not None: - module_identifier = '.'.join(get_relative_path_segments(self.module_path, self.src_directory)) + module_identifier = '.'.join( + get_relative_path_segments( + self.module_path, + self.src_directory + ) + ) if module_identifier.endswith('.py'): md += module_identifier[:-3] else: @@ -836,7 +987,9 @@ def _generate_package_namespace_documentation( documentation_directory: pathlib.Path, root_directory: pathlib.Path, dir: pathlib.Path, - include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(), + include_sections: PythonDefinitionDocumentationIncludeSections = ( + PythonDefinitionDocumentationIncludeSections() + ), kwargs: dict = {} ) -> None: ''' @@ -860,7 +1013,9 @@ def _generate_package_namespace_documentation( for sub_namespace in namespace.sub_namespaces: if isinstance(sub_namespace, PythonPackageNamespace): - (documentation_directory / sub_namespace.name).mkdir(mode = 0o700, exist_ok = True) + ( + documentation_directory / sub_namespace.name + ).mkdir(mode = 0o700, exist_ok = True) _generate_package_namespace_documentation( sub_namespace, documentation_directory / sub_namespace.name, @@ -897,7 +1052,9 @@ class PythonDocumentationGenerator: self, project_directory: pathlib.Path, docs_directory: pathlib.Path, - include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(), + include_sections: PythonDefinitionDocumentationIncludeSections = ( + PythonDefinitionDocumentationIncludeSections() + ), **kwargs ) -> None: self.project_directory = pathlib.Path(str(project_directory)) diff --git a/src/jcloud_docsgen/core/python/arguments.py b/src/jcloud_docsgen/core/python/arguments.py index 5cef187..869660a 100644 --- a/src/jcloud_docsgen/core/python/arguments.py +++ b/src/jcloud_docsgen/core/python/arguments.py @@ -17,7 +17,11 @@ from enum import Enum from typing import Union import ast from ...utils import assert_that_is_instance -from ...exceptions import InvalidPythonIdentifierError, InvalidPythonAnnotationError, PythonArgumentStructureError +from ...exceptions import ( + InvalidPythonAnnotationError, + InvalidPythonIdentifierError, + PythonArgumentStructureError +) import keyword import types @@ -48,7 +52,13 @@ class PythonFunctionArgument: :type annotation: Union[str, None] ''' - def __init__(self, name: str, kind: PythonArgumentKind, default: Union[str, None], annotation: Union[str, None]) -> None: + def __init__( + self, + name: str, + kind: PythonArgumentKind, + default: Union[str, None], + annotation: Union[str, None] + ) -> None: assert_that_is_instance(name, str) assert_that_is_instance(kind, PythonArgumentKind) assert_that_is_instance(default, (str, types.NoneType)) @@ -56,26 +66,43 @@ class PythonFunctionArgument: # check whether name is a valid identifier if not name or not name.isidentifier() or keyword.iskeyword(name): - raise InvalidPythonIdentifierError('invalid identifier', identifier = name) + raise InvalidPythonIdentifierError( + 'invalid identifier', + identifier = name + ) # check whether the annotation is a valid annotation # Currently, it is only checked whether the parameter is not # empty. if annotation is not None and not annotation: - raise InvalidPythonAnnotationError('invalid annotation', annotation = annotation) + raise InvalidPythonAnnotationError( + 'invalid annotation', + annotation = annotation + ) self.name = name self.kind = kind self.default = default self.annotation = annotation - if self.kind in (PythonArgumentKind.VARARG, PythonArgumentKind.KWARGS) and self.default is not None: - raise PythonArgumentStructureError('* or ** arguments cannot have default values', definition = self.signature_repr()) + if self.kind in ( + PythonArgumentKind.VARARG, + PythonArgumentKind.KWARGS + ) and self.default is not None: + raise PythonArgumentStructureError( + '* or ** arguments cannot have default values', + definition = self.signature_repr() + ) def __eq__(self, value: PythonFunctionArgument) -> bool: if not isinstance(value, PythonFunctionArgument): return False - return self.name == value.name and self.kind == value.kind and self.default == value.default and self.annotation == value.annotation + return ( + self.name == value.name + and self.kind == value.kind + and self.default == value.default + and self.annotation == value.annotation + ) def __repr__(self) -> str: return type(self).__name__ + repr(( @@ -131,43 +158,128 @@ class PythonASTArgumentsListParser: :rtype: list[PythonFunctionArgument] ''' - defaults = [None for _ in range(len( - self.ast_arguments_list.posonlyargs + - self.ast_arguments_list.args + - self.ast_arguments_list.kwonlyargs - ) - len(self.ast_arguments_list.defaults) - len(self.ast_arguments_list.kw_defaults))] + \ - [ast.unparse(d) for d in self.ast_arguments_list.defaults] + \ - [ast.unparse(d) if d is not None else None for d in self.ast_arguments_list.kw_defaults] + defaults = ( + [ + None + for _ in range(len( + self.ast_arguments_list.posonlyargs + + self.ast_arguments_list.args + + self.ast_arguments_list.kwonlyargs + ) - len( + self.ast_arguments_list.defaults + ) - len( + self.ast_arguments_list.kw_defaults + )) + ] + + [ + ast.unparse(d) + for d in self.ast_arguments_list.defaults + ] + + [ + ast.unparse(d) + if d is not None + else None + for d in self.ast_arguments_list.kw_defaults + ] + ) i = 0 arguments = [] for arg in self.ast_arguments_list.posonlyargs: if arg.annotation is None: - arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.POSITIONAL_ONLY, defaults[i], None)) + arguments.append( + PythonFunctionArgument( + arg.arg, + PythonArgumentKind.POSITIONAL_ONLY, + defaults[i], + None + ) + ) else: - arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.POSITIONAL_ONLY, defaults[i], ast.unparse(arg.annotation))) + arguments.append( + PythonFunctionArgument( + arg.arg, + PythonArgumentKind.POSITIONAL_ONLY, + defaults[i], + ast.unparse(arg.annotation))) i += 1 for arg in self.ast_arguments_list.args: if arg.annotation is None: - arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.NORMAL, defaults[i], None)) + arguments.append( + PythonFunctionArgument( + arg.arg, + PythonArgumentKind.NORMAL, + defaults[i], + None + ) + ) else: - arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.NORMAL, defaults[i], ast.unparse(arg.annotation))) + arguments.append( + PythonFunctionArgument( + arg.arg, + PythonArgumentKind.NORMAL, + defaults[i], + ast.unparse(arg.annotation) + ) + ) i += 1 if self.ast_arguments_list.vararg is not None: if self.ast_arguments_list.vararg.annotation is None: - arguments.append(PythonFunctionArgument(self.ast_arguments_list.vararg.arg, PythonArgumentKind.VARARG, None, None)) + arguments.append( + PythonFunctionArgument( + self.ast_arguments_list.vararg.arg, + PythonArgumentKind.VARARG, + None, + None + ) + ) else: - arguments.append(PythonFunctionArgument(self.ast_arguments_list.vararg.arg, PythonArgumentKind.VARARG, None, ast.unparse(self.ast_arguments_list.vararg.annotation))) + arguments.append( + PythonFunctionArgument( + self.ast_arguments_list.vararg.arg, + PythonArgumentKind.VARARG, + None, + ast.unparse(self.ast_arguments_list.vararg.annotation) + ) + ) for arg in self.ast_arguments_list.kwonlyargs: if arg.annotation is None: - arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.KEYWORD_ONLY, defaults[i], None)) + arguments.append( + PythonFunctionArgument( + arg.arg, + PythonArgumentKind.KEYWORD_ONLY, + defaults[i], + None + ) + ) else: - arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.KEYWORD_ONLY, defaults[i], ast.unparse(arg.annotation))) + arguments.append( + PythonFunctionArgument( + arg.arg, + PythonArgumentKind.KEYWORD_ONLY, + defaults[i], + ast.unparse(arg.annotation) + ) + ) i += 1 if self.ast_arguments_list.kwarg is not None: if self.ast_arguments_list.kwarg.annotation is None: - arguments.append(PythonFunctionArgument(self.ast_arguments_list.kwarg.arg, PythonArgumentKind.KWARGS, None, None)) + arguments.append( + PythonFunctionArgument( + self.ast_arguments_list.kwarg.arg, + PythonArgumentKind.KWARGS, + None, + None + ) + ) else: - arguments.append(PythonFunctionArgument(self.ast_arguments_list.kwarg.arg, PythonArgumentKind.KWARGS, None, ast.unparse(self.ast_arguments_list.kwarg.annotation))) + arguments.append( + PythonFunctionArgument( + self.ast_arguments_list.kwarg.arg, + PythonArgumentKind.KWARGS, + None, + ast.unparse(self.ast_arguments_list.kwarg.annotation) + ) + ) return arguments diff --git a/src/jcloud_docsgen/core/python/definitions.py b/src/jcloud_docsgen/core/python/definitions.py index fc06b60..04fc9df 100644 --- a/src/jcloud_docsgen/core/python/definitions.py +++ b/src/jcloud_docsgen/core/python/definitions.py @@ -59,9 +59,17 @@ class PythonDefinition: Python identifier. ''' - def __init__(self, name: str, doc: Union[PythonDocstring, None], decorators: list[str]) -> None: + def __init__( + self, + name: str, + doc: Union[PythonDocstring, None], + decorators: list[str] + ) -> None: if keyword.iskeyword(name) or not name.isidentifier() or not name: - raise InvalidPythonIdentifierError('invalid identifier', identifier = name) + raise InvalidPythonIdentifierError( + 'invalid identifier', + identifier = name + ) assert_that_is_instance(doc, (PythonDocstring, types.NoneType)) @@ -119,7 +127,14 @@ class PythonFunctionDefinition(PythonDefinition): Python identifier. ''' - def __init__(self, name: str, args: list[PythonFunctionArgument], returns: Union[str, None], doc: Union[PythonDocstring, None], decorators: list[str]) -> None: + def __init__( + self, + name: str, + args: list[PythonFunctionArgument], + returns: Union[str, None], + doc: Union[PythonDocstring, None], + decorators: list[str] + ) -> None: super().__init__(name, doc, decorators) self.args = args self.returns = returns @@ -189,7 +204,14 @@ class PythonClassDefinition(PythonDefinition): Python identifier. ''' - def __init__(self, name: str, bases: list[str], doc: Union[PythonDocstring, None], decorators: list[str], body: list[PythonDefinition]) -> None: + def __init__( + self, + name: str, + bases: list[str], + doc: Union[PythonDocstring, None], + decorators: list[str], + body: list[PythonDefinition] + ) -> None: super().__init__(name, doc, decorators) self.bases = bases self.body = body @@ -227,6 +249,9 @@ class PythonClassDefinition(PythonDefinition): else PythonClassDefinition.from_node(expr) for expr in node.body - if isinstance(expr, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) + if isinstance( + expr, + (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef) + ) ] ) diff --git a/src/jcloud_docsgen/core/python/namespaces.py b/src/jcloud_docsgen/core/python/namespaces.py index 222b665..d6387a5 100644 --- a/src/jcloud_docsgen/core/python/namespaces.py +++ b/src/jcloud_docsgen/core/python/namespaces.py @@ -13,7 +13,11 @@ # limitations under the License. from __future__ import annotations -from ...exceptions import InvalidNamespaceError, NamespaceNotFoundError, NamespaceExistsError +from ...exceptions import ( + InvalidNamespaceError, + NamespaceNotFoundError, + NamespaceExistsError +) from ...utils import assert_that_is_instance from typing import Union, TypeAlias, Dict, List @@ -34,14 +38,23 @@ class PythonNamespace: assert_that_is_instance(name, str) if not name.strip(): - raise InvalidNamespaceError('invalid namespace', namespace_identifier = name) + raise InvalidNamespaceError( + 'invalid namespace', + namespace_identifier = name + ) self.name = name def __repr__(self) -> str: return f'{type(self).__name__}({self.name!r})' -PythonPackageNamespaceDictType: TypeAlias = Dict[str, Union[str, 'PythonPackageNamespaceDictType']] +PythonPackageNamespaceDictType: TypeAlias = Dict[ + str, + Union[ + str, + 'PythonPackageNamespaceDictType' + ] +] class PythonPackageNamespace(PythonNamespace): ''' @@ -53,7 +66,11 @@ class PythonPackageNamespace(PythonNamespace): :type sub_namespaces: list[PythonNamespace] ''' - def __init__(self, name: str, sub_namespaces: List[PythonNamespace]) -> None: + def __init__( + self, + name: str, + sub_namespaces: List[PythonNamespace] + ) -> None: assert_that_is_instance(sub_namespaces, list) super().__init__(name) @@ -94,7 +111,10 @@ class PythonPackageNamespace(PythonNamespace): else: return sub_namespace - def _sub_namespaces_names(self, package: PythonPackageNamespace) -> PythonPackageNamespaceDictType: + def _sub_namespaces_names( + self, + package: PythonPackageNamespace + ) -> PythonPackageNamespaceDictType: ''' Returns the names of all sub-namespaces of the specified package namespace and keeps the package structure. @@ -107,7 +127,11 @@ class PythonPackageNamespace(PythonNamespace): ''' return { - sn.name: self._sub_namespaces_names(sn) if isinstance(sn, PythonPackageNamespace) else sn.name + sn.name: ( + self._sub_namespaces_names(sn) + if isinstance(sn, PythonPackageNamespace) + else sn.name + ) for sn in package._sub_namespaces } @@ -151,7 +175,10 @@ class PythonPackageNamespace(PythonNamespace): if sub.name == name: return sub - raise NamespaceNotFoundError('no such namespace', namespace_identifier = name) + raise NamespaceNotFoundError( + 'no such namespace', + namespace_identifier = name + ) def add(self, namespace: PythonNamespace) -> None: ''' @@ -166,7 +193,10 @@ class PythonPackageNamespace(PythonNamespace): ''' try: self.__getitem__(namespace.name) - raise NamespaceExistsError('namespace already exists', namespace_identifier = namespace.name) + raise NamespaceExistsError( + 'namespace already exists', + namespace_identifier = namespace.name + ) except NamespaceNotFoundError: self._sub_namespaces.append(namespace) diff --git a/src/jcloud_docsgen/exceptions.py b/src/jcloud_docsgen/exceptions.py index 6cb7d11..2feb8a6 100644 --- a/src/jcloud_docsgen/exceptions.py +++ b/src/jcloud_docsgen/exceptions.py @@ -24,7 +24,15 @@ class NamespaceError(ValueError): if not self.args: return '' else: - return f'{self.args[0]}{": " if self.namespace_identifier and self.args[0] else ""}{self.namespace_identifier if self.args[0] else ""}' + return f'{self.args[0]}{ + ": " + if self.namespace_identifier and self.args[0] + else "" + }{ + self.namespace_identifier + if self.args[0] + else "" + }' class InvalidNamespaceError(NamespaceError): ... class NamespaceNotFoundError(NamespaceError): ... @@ -42,7 +50,15 @@ class PythonIdentifierError(ValueError): if not self.args: return '' else: - return f'{self.args[0]}{": " if self.identifier and self.args[0] else ""}{self.identifier if self.args[0] else ""}' + return f'{self.args[0]}{ + ": " + if self.identifier and self.args[0] + else "" + }{ + self.identifier + if self.args[0] + else "" + }' class InvalidPythonIdentifierError(PythonIdentifierError): ... @@ -58,7 +74,15 @@ class PythonAnnotationError(ValueError): if not self.args: return '' else: - return f'{self.args[0]}{": " if self.annotation and self.args[0] else ""}{self.annotation if self.args[0] else ""}' + return f'{self.args[0]}{ + ": " + if self.annotation and self.args[0] + else "" + }{ + self.annotation + if self.args[0] + else "" + }' class InvalidPythonAnnotationError(PythonAnnotationError): ... @@ -74,6 +98,14 @@ class PythonDefinitionError(ValueError): if not self.args: return '' else: - return f'{self.args[0]}{": " if self.definition and self.args[0] else ""}{self.definition if self.args[0] else ""}' + return f'{self.args[0]}{ + ": " + if self.definition and self.args[0] + else "" + }{ + self.definition + if self.args[0] + else "" + }' class PythonArgumentStructureError(PythonDefinitionError): ... diff --git a/src/jcloud_docsgen/utils.py b/src/jcloud_docsgen/utils.py index 29c8e98..fae9140 100644 --- a/src/jcloud_docsgen/utils.py +++ b/src/jcloud_docsgen/utils.py @@ -38,7 +38,11 @@ class ExistingDirectory(pathlib.Path): ''' def __init__(self, path, *args, **kwargs) -> None: if not os.path.exists(path): - raise FileNotFoundError(errno.ENOENT, 'no such file or directory', path) + raise FileNotFoundError( + errno.ENOENT, + 'no such file or directory', + path + ) if not os.path.isdir(path): raise NotADirectoryError(errno.ENOTDIR, 'not a directory', path) super().__init__(*(path, *args), **kwargs) @@ -54,7 +58,11 @@ class ExistingFile(pathlib.Path): ''' def __init__(self, path, *args, **kwargs) -> None: if not os.path.exists(path): - raise FileNotFoundError(errno.ENOENT, 'no such file or directory', path) + raise FileNotFoundError( + errno.ENOENT, + 'no such file or directory', + path + ) if not os.path.isfile(path): raise IsADirectoryError(errno.EISDIR, 'is a directory', path) super().__init__(*(path, *args), **kwargs) @@ -74,7 +82,11 @@ def _quote(string: str, quotation_mark: str) -> str: return quotation_mark + string + quotation_mark -def human_readable_list(ls: list, final_separator: str = 'and', quotation_mark: str = '') -> str: +def human_readable_list( + ls: list, + final_separator: str = 'and', + quotation_mark: str = '' +) -> str: ''' Generates a human-readable list from a list. @@ -101,7 +113,10 @@ def human_readable_list(ls: list, final_separator: str = 'and', quotation_mark: return ', '.join( [ - _quote(str(obj), quotation_mark) # quote the string representation of the object + _quote( + str(obj), + quotation_mark + ) # quote the string representation of the object for obj in ls[:-1] # do not include the last element of the list ] ) + ' ' + final_separator + ' ' + _quote(str(ls[-1]), quotation_mark) @@ -120,9 +135,19 @@ def _list_type_names(types: list[type]) -> list[str]: return [tp.__name__ for tp in types] -def assert_that_is_instance(obj: object, class_or_tuple: Union[type, types.UnionType, tuple[type, ...]]) -> None: +def assert_that_is_instance( + obj: object, + class_or_tuple: Union[ + type, + types.UnionType, + tuple[type, ...] + ] +) -> None: if not isinstance(class_or_tuple, Union[type, types.UnionType, tuple]): - raise TypeError(f'class_or_tuple: expected \'Union[type, types.UnionType, tuple[type, ...]]\', \'got {type(class_or_tuple).__name__}\'') + raise TypeError( + f'class_or_tuple: expected \'Union[type, types.UnionType, ' + f'tuple[type, ...]]\', \'got {type(class_or_tuple).__name__}\'' + ) if not isinstance(obj, class_or_tuple): if isinstance(class_or_tuple, (tuple, types.UnionType)): if isinstance(class_or_tuple, types.UnionType): @@ -131,10 +156,15 @@ def assert_that_is_instance(obj: object, class_or_tuple: Union[type, types.Union exception_message_expected = 'either ' else: exception_message_expected = '' - exception_message_expected += human_readable_list(_list_type_names(class_or_tuple), 'or', '\'') + exception_message_expected += human_readable_list( + _list_type_names(class_or_tuple), 'or', '\'') else: exception_message_expected = '\'' + class_or_tuple.__name__ + '\'' - raise TypeError(f'expected {exception_message_expected}, got \'{type(obj).__name__}\'') + raise TypeError( + f'expected {exception_message_expected}, got \'{type( + obj + ).__name__}\'' + ) def non_empty_str(value: Union[str, None]) -> Union[str, None]: ''' @@ -160,7 +190,10 @@ def non_empty_str(value: Union[str, None]) -> Union[str, None]: return value -def get_relative_path_segments(path: pathlib.Path, root: pathlib.Path) -> list[str]: +def get_relative_path_segments( + path: pathlib.Path, + root: pathlib.Path +) -> list[str]: ''' Returns the path segments relative to ``root``. diff --git a/tests/unit/cli/test_python.py b/tests/unit/cli/test_python.py index a797249..2b4b98f 100644 --- a/tests/unit/cli/test_python.py +++ b/tests/unit/cli/test_python.py @@ -16,7 +16,9 @@ import pathlib import pytest from jcloud_docsgen.cli.cli import create_argument_parser, main as cli_main from jcloud_docsgen.cli.languages.python import parse_sections_list -from jcloud_docsgen.core.python import PythonDefinitionDocumentationIncludeSections +from jcloud_docsgen.core.python import ( + PythonDefinitionDocumentationIncludeSections +) @pytest.mark.parametrize('include,exclude,expected', [ ( diff --git a/tests/unit/core/python/_core/test_PythonDefinitionDocumentationGenerator.py b/tests/unit/core/python/_core/test_PythonDefinitionDocumentationGenerator.py index f13141f..0fb7465 100644 --- a/tests/unit/core/python/_core/test_PythonDefinitionDocumentationGenerator.py +++ b/tests/unit/core/python/_core/test_PythonDefinitionDocumentationGenerator.py @@ -12,9 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.jcloud_docsgen.core.python._core import PythonDefinitionDocumentationGenerator, PythonDefinitionDocumentationIncludeSections -from src.jcloud_docsgen.core.python.definitions import PythonFunctionDefinition, PythonAsyncFunctionDefinition, PythonClassDefinition -from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind +from src.jcloud_docsgen.core.python._core import ( + PythonDefinitionDocumentationGenerator, + PythonDefinitionDocumentationIncludeSections +) +from src.jcloud_docsgen.core.python.definitions import ( + PythonFunctionDefinition, + PythonAsyncFunctionDefinition, + PythonClassDefinition +) +from src.jcloud_docsgen.core.python.arguments import ( + PythonFunctionArgument, + PythonArgumentKind +) from src.jcloud_docsgen.core.python.docstrings import PythonDocstring import pytest @@ -37,113 +47,749 @@ def _signature_section(signature: str) -> str: ```''' @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, [])), 'a()'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], 'str', None, [])), 'a() -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None) - ], 'str', None, [])), 'a(a) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str') - ], 'str', None, [])), 'a(a: str) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '\'42\'', None) - ], 'str', None, [])), 'a(a = \'42\') -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '\'42\'', 'str') - ], 'str', None, [])), 'a(a: str = \'42\') -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, '\'42\'', 'str'), - ], 'str', None, [])), 'a(a: str = \'42\', /) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], 'str', None, [])), 'a(a, /) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], None, None, [])), 'a(a, /)'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('definition', PythonArgumentKind.NORMAL, None, 'PythonDefinition'), - PythonFunctionArgument('allow_html', PythonArgumentKind.KEYWORD_ONLY, 'True', 'bool'), - PythonFunctionArgument('level', PythonArgumentKind.KEYWORD_ONLY, '1', 'int'), - ], 'None', None, [])), '__init__(self, definition: PythonDefinition, *, allow_html: bool = True, level: int = 1) -> None'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ) + ), + 'a()' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + 'str', + None, + [] + ) + ), + 'a() -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + 'str', + None, + [] + ) + ), + 'a(a) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'str' + ) + ], + 'str', + None, + [] + ) + ), + 'a(a: str) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '\'42\'', + None + ) + ], + 'str', + None, + [] + ) + ), + 'a(a = \'42\') -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '\'42\'', + 'str' + ) + ], + 'str', + None, + [] + ) + ), + 'a(a: str = \'42\') -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + '\'42\'', + 'str' + ) + ], + 'str', + None, + [] + ) + ), + 'a(a: str = \'42\', /) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ) + ], + 'str', + None, + [] + ) + ), + 'a(a, /) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ) + ], + None, + None, + [] + ) + ), + 'a(a, /)' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'definition', + PythonArgumentKind.NORMAL, + None, + 'PythonDefinition' + ), + PythonFunctionArgument( + 'allow_html', + PythonArgumentKind.KEYWORD_ONLY, + 'True', + 'bool' + ), + PythonFunctionArgument( + 'level', + PythonArgumentKind.KEYWORD_ONLY, + '1', + 'int' + ) + ], + 'None', + None, + [] + ) + ), + ( + '__init__(self, definition: PythonDefinition, *, ' + 'allow_html: bool = True, level: int = 1) -> None' + ) + ), ]) -def test_PythonDefinitionDocumentationGenerator_generate_signature(definition_documentation_generator, expected): +def test_PythonDefinitionDocumentationGenerator_generate_signature( + definition_documentation_generator, + expected +): assert definition_documentation_generator.generate_signature() == expected -@pytest.mark.parametrize('definition_documentation_generator,expected_exception,expected_exception_msg', [ - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', [], None, [], [])), TypeError, 'Cannot generate a signature of a class definition.') -]) -def test_PythonDefinitionDocumentationGenerator_generate_signature_exceptions(definition_documentation_generator, expected_exception, expected_exception_msg): +@pytest.mark.parametrize( + ( + 'definition_documentation_generator,exp' + 'ected_exception,expected_exception_msg' + ), + [ + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + [], + None, + [], + [] + ) + ), + TypeError, + 'Cannot generate a signature of a class definition.' + ) + ] +) +def test_PythonDefinitionDocumentationGenerator_generate_signature_exceptions( + definition_documentation_generator, + expected_exception, + expected_exception_msg +): with pytest.raises(expected_exception) as exc_info: definition_documentation_generator.generate_signature() assert str(exc_info.value) == expected_exception_msg @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, [])), 'a()'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], 'str', None, [])), 'a() -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None) - ], 'str', None, [])), 'a(a) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str') - ], 'str', None, [])), 'a(a: str) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '\'42\'', None) - ], 'str', None, [])), 'a(a = \'42\') -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '\'42\'', 'str') - ], 'str', None, [])), 'a(a: str = \'42\') -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, '\'42\'', 'str'), - ], 'str', None, [])), 'a(a: str = \'42\', /) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], 'str', None, [])), 'a(a, /) -> str'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], None, None, [])), 'a(a, /)'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('definition', PythonArgumentKind.NORMAL, None, 'PythonDefinition'), - PythonFunctionArgument('allow_html', PythonArgumentKind.KEYWORD_ONLY, 'True', 'bool'), - PythonFunctionArgument('level', PythonArgumentKind.KEYWORD_ONLY, '1', 'int'), - ], 'None', None, [])), '__init__(self, definition: PythonDefinition, *, allow_html: bool = True, level: int = 1) -> None'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ) + ), + 'a()' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + 'str', + None, + [] + ) + ), + 'a() -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + 'str', + None, + [] + ) + ), + 'a(a) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'str' + ) + ], + 'str', + None, + [] + ) + ), + 'a(a: str) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '\'42\'', + None + ) + ], + 'str', + None, + [] + ) + ), + 'a(a = \'42\') -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '\'42\'', + 'str' + ) + ], + 'str', + None, + [] + ) + ), + 'a(a: str = \'42\') -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + '\'42\'', + 'str' + ) + ], + 'str', + None, + [] + ) + ), + 'a(a: str = \'42\', /) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ) + ], + 'str', + None, + [] + ) + ), + 'a(a, /) -> str' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ) + ], + None, + None, + [] + ) + ), + 'a(a, /)' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'definition', + PythonArgumentKind.NORMAL, + None, + 'PythonDefinition' + ), + PythonFunctionArgument( + 'allow_html', + PythonArgumentKind.KEYWORD_ONLY, + 'True', + 'bool' + ), + PythonFunctionArgument( + 'level', + PythonArgumentKind.KEYWORD_ONLY, + '1', + 'int' + ), + ], + 'None', + None, + [] + ) + ), + ( + '__init__(self, definition: PythonDefinition, *, ' + 'allow_html: bool = True, level: int = 1) -> None' + ) + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_signature_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_signature_section() == _signature_section(expected) +def test_PythonDefinitionDocumentationGenerator__generate_signature_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_signature_section() + == _signature_section(expected) + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), allow_html = False, skip_empty_sections = True), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(''), []), allow_html = False, skip_empty_sections = True), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a'), []), allow_html = False, skip_empty_sections = True), '## Docstring\n\na'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a\nb'), []), allow_html = False, skip_empty_sections = True), '## Docstring\n\na\nb'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + allow_html = False, + skip_empty_sections = True + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(''), + [] + ), + allow_html = False, + skip_empty_sections = True + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a'), + [] + ), + allow_html = False, + skip_empty_sections = True + ), + '## Docstring\n\na' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a\nb'), + [] + ), + allow_html = False, + skip_empty_sections = True + ), + '## Docstring\n\na\nb' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), allow_html = True, skip_empty_sections = True), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(''), []), allow_html = True, skip_empty_sections = True), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a'), []), allow_html = True, skip_empty_sections = True), '

Docstring

a
'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a\nb'), []), allow_html = True, skip_empty_sections = True), '

Docstring

a\nb
'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + allow_html = True, + skip_empty_sections = True + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(''), + [] + ), + allow_html = True, + skip_empty_sections = True + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a'), + [] + ), + allow_html = True, + skip_empty_sections = True + ), + ( + '

Docstring

a
' + ) + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a\nb'), + [] + ), + allow_html = True, + skip_empty_sections = True + ), + ( + '

Docstring

a\nb
' + ) + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), allow_html = False, skip_empty_sections = False), '## Docstring\n\nThere is no docstring to show.'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(''), []), allow_html = False, skip_empty_sections = False), '## Docstring\n\nThere is no docstring to show.'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a'), []), allow_html = False, skip_empty_sections = False), '## Docstring\n\na'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a\nb'), []), allow_html = False, skip_empty_sections = False), '## Docstring\n\na\nb'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + allow_html = False, + skip_empty_sections = False + ), + '## Docstring\n\nThere is no docstring to show.' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(''), + [] + ), + allow_html = False, + skip_empty_sections = False + ), + '## Docstring\n\nThere is no docstring to show.' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a'), + [] + ), + allow_html = False, + skip_empty_sections = False + ), + '## Docstring\n\na' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a\nb'), + [] + ), + allow_html = False, + skip_empty_sections = False + ), + '## Docstring\n\na\nb' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), allow_html = True, skip_empty_sections = False), '

Docstring

There is no docstring to show.
'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(''), []), allow_html = True, skip_empty_sections = False), '

Docstring

There is no docstring to show.
'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a'), []), allow_html = True, skip_empty_sections = False), '

Docstring

a
'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a\nb'), []), allow_html = True, skip_empty_sections = False), '

Docstring

a\nb
'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + allow_html = True, + skip_empty_sections = False + ), + ( + '

Docstring<' + '/h2>

There is no docstring to show.
' + ) + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(''), + [] + ), + allow_html = True, + skip_empty_sections = False + ), + ( + '

Docstring<' + '/h2>

There is no docstring to show.
' + ) + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a'), + [] + ), + allow_html = True, + skip_empty_sections = False + ), + ( + '

Docstring

a
' + ) + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a\nb'), + [] + ), + allow_html = True, + skip_empty_sections = False + ), + ( + '

Docstring

a\nb
' + ) + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_docstring_section() == expected +def test_PythonDefinitionDocumentationGenerator__generate_docstring_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_docstring_section() + == expected + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, [])), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), skip_empty_sections = False), '## Parameters\n\nThis function takes no parameters.'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + skip_empty_sections = False + ), + '## Parameters\n\nThis function takes no parameters.' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None) - ], None, None, [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + None, + None, + [] + ) + ), + '''## Parameters - Parameter: `a` @@ -153,11 +799,27 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: -- - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '1', None) - ], None, None, [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '1', + None + ) + ], + None, + None, + [] + ) + ), + '''## Parameters - Parameter: `a` @@ -167,11 +829,27 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: `1` - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int') - ], None, None, [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ) + ], + None, + None, + [] + ) + ), + '''## Parameters - Parameter: `a` @@ -181,11 +859,27 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: -- - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '1', 'int') - ], None, None, [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '1', + 'int' + ) + ], + None, + None, + [] + ) + ), + '''## Parameters - Parameter: `a` @@ -195,12 +889,33 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: `1` - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], None, None, [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ) + ], + None, + None, + [] + ) + ), + '''## Parameters - Parameter: `a` @@ -220,12 +935,33 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: -- - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], None, PythonDocstring(':param b: a parameter\n:type b: int'), [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ) + ], + None, + PythonDocstring(':param b: a parameter\n:type b: int'), + [] + ) + ), + '''## Parameters - Parameter: `a` @@ -245,12 +981,34 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: -- - Description: a parameter'''), + Description: a parameter''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], None, PythonDocstring(':param b:\n:type b: int'), [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + ], + None, + PythonDocstring( + ':param b:\n:type b: int' + ), + [] + ) + ), '''## Parameters - Parameter: `a` @@ -270,12 +1028,32 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: -- - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], None, PythonDocstring(':param b: \n:type b: int'), [])), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + ], + None, + PythonDocstring(':param b: \n:type b: int'), + [] + ) + ), '''## Parameters - Parameter: `a` @@ -295,31 +1073,98 @@ def test_PythonDefinitionDocumentationGenerator__generate_docstring_section(defi Default value: -- - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - ], None, PythonDocstring(':param b: a parameter\n:type b: int'), []), allow_tables = True), '''## Parameters + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + ], + None, + PythonDocstring(':param b: a parameter\n:type b: int'), + [] + ), + allow_tables = True + ), + '''## Parameters | Parameter | Type | Kind | Default value | Description | | --- | --- | --- | --- | --- | | `a` | -- | normal | -- | -- | -| `b` | `int` | positional-only | -- | a parameter |'''), +| `b` | `int` | positional-only | -- | a parameter |''' + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_params_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_params_section() == expected +def test_PythonDefinitionDocumentationGenerator__generate_params_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_params_section() + == expected + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, [])), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), skip_empty_sections = False), '## Exceptions\n\nThis function raises no exceptions.'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(':raises E: a'), [])), '''## Exceptions + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + skip_empty_sections = False + ), + '## Exceptions\n\nThis function raises no exceptions.' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(':raises E: a'), [])), '''## Exceptions - Exception: `E` Description: a'''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(':raises E:\n:raises E2:'), [])), '''## Exceptions + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(':raises E:\n:raises E2:'), + [] + ) + ), + '''## Exceptions - Exception: `E` @@ -327,9 +1172,20 @@ def test_PythonDefinitionDocumentationGenerator__generate_params_section(definit - Exception: `E2` - Description: --'''), + Description: --''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(':raises E:\n:raises E2: a'), [])), '''## Exceptions + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(':raises E:\n:raises E2: a'), + [] + ) + ), + '''## Exceptions - Exception: `E` @@ -337,40 +1193,187 @@ def test_PythonDefinitionDocumentationGenerator__generate_params_section(definit - Exception: `E2` - Description: a'''), + Description: a''' + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_exceptions_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_exceptions_section() == expected +def test_PythonDefinitionDocumentationGenerator__generate_exceptions_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_exceptions_section() + == expected + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, [])), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(''), [])), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('Examples\n--------'), [])), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('Examples\n--------\n'), [])), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('Examples\n--------\n\n'), [])), ''), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(''), + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('Examples\n--------'), + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('Examples\n--------\n'), + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('Examples\n--------\n\n'), + [] + ) + ), + '' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), skip_empty_sections = False), '## Examples\n\nThere are no examples.'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(''), []), skip_empty_sections = False), '## Examples\n\nThere are no examples.'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('Examples\n--------'), []), skip_empty_sections = False), '## Examples\n\nThere are no examples.'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('Examples\n--------\n'), []), skip_empty_sections = False), '## Examples\n\nThere are no examples.'), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('Examples\n--------\n\n'), []), skip_empty_sections = False), '## Examples\n\nThere are no examples.'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + skip_empty_sections = False + ), + '## Examples\n\nThere are no examples.' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(''), + [] + ), + skip_empty_sections = False + ), + '## Examples\n\nThere are no examples.' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('Examples\n--------'), + [] + ), + skip_empty_sections = False + ), + '## Examples\n\nThere are no examples.' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('Examples\n--------\n'), + [] + ), + skip_empty_sections = False + ), + '## Examples\n\nThere are no examples.' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('Examples\n--------\n\n'), + [] + ), + skip_empty_sections = False + ), + '## Examples\n\nThere are no examples.' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('''Examples + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring( + '''Examples -------- >>> a() -None'''), [])), '''## Examples +None''' + ), + [] + ) + ), + '''## Examples ```python >>> a() -```'''), +```''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('''Examples + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring( + '''Examples -------- >>> a() None >>> a() -None'''), [])), '''## Examples +None''' + ), + [] + ) + ), + '''## Examples ```python >>> a() @@ -380,15 +1383,28 @@ None'''), [])), '''## Examples ```python >>> a() -```'''), +```''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('''Examples + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring( + '''Examples -------- >>> a() None >>> b() -1'''), [])), '''## Examples +1''' + ), + [] + ) + ), + '''## Examples ```python >>> a() @@ -398,46 +1414,138 @@ None ```python >>> b() -```'''), - +```''' + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_examples_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_examples_section() == expected +def test_PythonDefinitionDocumentationGenerator__generate_examples_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_examples_section() + == expected + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], 'int', None, [])), '''## Returns + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + 'int', + None, + [] + ) + ), + '''## Returns -Return type: `int`'''), +Return type: `int`''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], 'int', PythonDocstring(':return: value'), [])), '''## Returns + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + 'int', + PythonDocstring(':return: value'), + [] + ) + ), + '''## Returns value -Return type: `int`'''), +Return type: `int`''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], 'int', PythonDocstring(':return: value\n:rtype: str'), [])), '''## Returns + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + 'int', + PythonDocstring(':return: value\n:rtype: str'), + [] + ) + ), + '''## Returns value -Return type: `int`'''), +Return type: `int`''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(':return: value\n:rtype: str'), [])), '''## Returns + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(':return: value\n:rtype: str'), + [] + ) + ), + '''## Returns value -Return type: `str`'''), +Return type: `str`''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring(':return: value'), [])), '''## Returns + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring(':return: value'), + [] + ) + ), + '''## Returns -value'''), +value''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, PythonDocstring('a'), []), skip_empty_sections = False), '## Returns\n\nThis function does not provide any information about its return value.'), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('a'), + [] + ), + skip_empty_sections = False + ), + ( + '## Returns\n\nThis function does not provi' + 'de any information about its return value.' + ) + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_returns_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_returns_section() == expected +def test_PythonDefinitionDocumentationGenerator__generate_returns_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_returns_section() + == expected + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, ['a', 'b'])), '''## Decorators + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + ['a', 'b'] + ) + ), + '''## Decorators ```python a @@ -445,50 +1553,194 @@ a ```python b -```'''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, ['a'])), '''## Decorators +```''' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + ['a'] + ) + ), + '''## Decorators ```python a -```'''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, [])), ''), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [], None, None, []), skip_empty_sections = False), '''## Decorators +```''' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + skip_empty_sections = False + ), + '''## Decorators -This function or class does not have any decorators.'''), +This function or class does not have any decorators.''' + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_decorators_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_decorators_section() == expected +def test_PythonDefinitionDocumentationGenerator__generate_decorators_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_decorators_section() + == expected + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', [], None, [], [])), ''), - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', [], None, [], []), skip_empty_sections = False), '''## Base classes + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + [], + None, + [], + [] + ) + ), + '' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + [], + None, + [], + [] + ), + skip_empty_sections = False + ), + '''## Base classes -This class does not have any base classes.'''), - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', ['a'], None, [], []), skip_empty_sections = False), '''## Base classes +This class does not have any base classes.''' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + ['a'], + None, + [], + [] + ), + skip_empty_sections = False + ), + '''## Base classes - `a`'''), - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', ['b'], None, [], []), skip_empty_sections = False), '''## Base classes + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + ['b'], + None, + [], + [] + ), + skip_empty_sections = False + ), + '''## Base classes -- `b`'''), - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', ['a', 'b'], None, [], []), skip_empty_sections = False), '''## Base classes +- `b`''' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + ['a', 'b'], + None, + [], + [] + ), + skip_empty_sections = False + ), + '''## Base classes - `a` -- `b`'''), - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', ['b', 'a'], None, [], []), skip_empty_sections = False), '''## Base classes +- `b`''' + ), + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + ['b', 'a'], + None, + [], + [] + ), + skip_empty_sections = False + ), + '''## Base classes - `b` -- `a`'''), +- `a`''' + ), ]) -def test_PythonDefinitionDocumentationGenerator__generate_bases_section(definition_documentation_generator, expected): - assert definition_documentation_generator._generate_bases_section() == expected +def test_PythonDefinitionDocumentationGenerator__generate_bases_section( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator._generate_bases_section() + == expected + ) @pytest.mark.parametrize('definition_documentation_generator,expected', [ - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -501,7 +1753,13 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), allow_html = False), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + allow_html = False + ), + '''# Function `a` A function. @@ -590,15 +1848,43 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), +:rtype: list''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -611,7 +1897,13 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)'])), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ) + ), + ( + '''# Function `a` A function. @@ -685,7 +1977,8 @@ decorate(1, 2, 3) -

Docstring

A function. +

Docstring

A ''' + '''function. Very long ... @@ -698,15 +1991,44 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list
'''), +:rtype: list
''' + ) + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -719,7 +2041,14 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), allow_html = False, allow_tables = True), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + allow_html = False, + allow_tables = True + ), + '''# Function `a` A function. @@ -775,15 +2104,43 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), +:rtype: list''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -796,7 +2153,14 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), allow_tables = True), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + allow_tables = True + ), + ( + '''# Function `a` A function. @@ -837,7 +2201,8 @@ decorate(1, 2, 3) -

Docstring

A function. +

Docstring

A ''' + '''function. Very long ... @@ -850,15 +2215,44 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list
'''), +:rtype: list
''' + ) + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -871,7 +2265,14 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), allow_html = False, skip_empty_sections = False), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + allow_html = False, + skip_empty_sections = False + ), + '''# Function `a` A function. @@ -962,15 +2363,43 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), +:rtype: list''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -983,7 +2412,14 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), skip_empty_sections = False), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + skip_empty_sections = False + ), + ( + '''# Function `a` A function. @@ -1059,7 +2495,8 @@ decorate(1, 2, 3) There are no examples. -

Docstring

A function. +

Docstring

A ''' + '''function. Very long ... @@ -1072,15 +2509,44 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list
'''), +:rtype: list
''' + ) + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -1093,7 +2559,15 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), allow_html = False, allow_tables = True, skip_empty_sections = False), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + allow_html = False, + allow_tables = True, + skip_empty_sections = False + ), + '''# Function `a` A function. @@ -1151,15 +2625,43 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), +:rtype: list''' + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -1172,7 +2674,15 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), allow_tables = True, skip_empty_sections = False), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + allow_tables = True, + skip_empty_sections = False + ), + ( + '''# Function `a` A function. @@ -1215,7 +2725,8 @@ decorate(1, 2, 3) There are no examples. -

Docstring

A function. +

Docstring

A ''' + '''function. Very long ... @@ -1228,15 +2739,44 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list
'''), +:rtype: list
''' + ) + ), - (PythonDefinitionDocumentationGenerator(PythonFunctionDefinition('a', [ - PythonFunctionArgument('hello', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('world', PythonArgumentKind.NORMAL, '1', None), - PythonFunctionArgument('something', PythonArgumentKind.KEYWORD_ONLY, '\'something\'', 'str'), - PythonFunctionArgument('value', PythonArgumentKind.KWARGS, None, None), - ], 'bool', PythonDocstring('''A function. + ( + PythonDefinitionDocumentationGenerator( + PythonFunctionDefinition( + 'a', + [ + PythonFunctionArgument( + 'hello', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'world', + PythonArgumentKind.NORMAL, + '1', + None + ), + PythonFunctionArgument( + 'something', + PythonArgumentKind.KEYWORD_ONLY, + '\'something\'', + 'str' + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.KWARGS, + None, + None + ), + ], + 'bool', + PythonDocstring( + '''A function. Very long ... @@ -1249,45 +2789,126 @@ Very long ... :raises ValueError: If the value is invalid. :return: The return value. -:rtype: list'''), ['decorate(1, 2, 3)']), PythonDefinitionDocumentationIncludeSections( - signature = False, - returns = False, - params = False, - exceptions = False, - examples = False, - docstring = False, - bases = False, - decorators = False -), allow_tables = True, skip_empty_sections = False), '''# Function `a` +:rtype: list''' + ), + ['decorate(1, 2, 3)'] + ), + PythonDefinitionDocumentationIncludeSections( + signature = False, + returns = False, + params = False, + exceptions = False, + examples = False, + docstring = False, + bases = False, + decorators = False + ), + allow_tables = True, + skip_empty_sections = False + ), + '''# Function `a` A function. -Very long ...'''), +Very long ...''' + ), - (PythonDefinitionDocumentationGenerator(PythonClassDefinition('A', ['Base1', 'Base2'], PythonDocstring('''A class. + ( + PythonDefinitionDocumentationGenerator( + PythonClassDefinition( + 'A', + ['Base1', 'Base2'], + PythonDocstring( + '''A class. Examples -------- >>> A() ->>> A()'''), ['dec', 'ora', 'tor'], [ - PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - ], None, None, []), - PythonFunctionDefinition('method', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '1', 'int') - ], 'str', None, ['decorator']), - PythonAsyncFunctionDefinition('method2', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - ], None, None, []), - PythonClassDefinition('B', ['Base'], None, [], [ - PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None) - ], None, None, []) - ]) - ]), allow_html = False, allow_tables = True, include_sections = PythonDefinitionDocumentationIncludeSections(bases = False)), '''# Class `A` +>>> A()''' + ), + ['dec', 'ora', 'tor'], + [ + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + ], + None, + None, + [] + ), + PythonFunctionDefinition( + 'method', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '1', + 'int' + ) + ], + 'str', + None, + ['decorator'] + ), + PythonAsyncFunctionDefinition( + 'method2', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + ], + None, + None, + [] + ), + PythonClassDefinition( + 'B', + ['Base'], + None, + [], + [ + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + None, + None, + [] + ) + ] + ) + ] + ), + allow_html = False, + allow_tables = True, + include_sections = PythonDefinitionDocumentationIncludeSections( + bases = False + ) + ), + '''# Class `A` A class. @@ -1407,7 +3028,14 @@ Examples -------- >>> A() ->>> A()''') +>>> A()''' + ) ]) -def test_PythonDefinitionDocumentationGenerator_generate_documentation(definition_documentation_generator, expected): - assert definition_documentation_generator.generate_documentation() == expected +def test_PythonDefinitionDocumentationGenerator_generate_documentation( + definition_documentation_generator, + expected +): + assert ( + definition_documentation_generator.generate_documentation() + == expected + ) diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py index cbdacaf..9e2c79d 100644 --- a/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py @@ -13,28 +13,98 @@ # limitations under the License. from src.jcloud_docsgen.core.python import PythonDocumentationGenerator -from src.jcloud_docsgen.core.python.namespaces import PythonModuleNamespace, PythonPackageNamespace +from src.jcloud_docsgen.core.python.namespaces import ( + PythonModuleNamespace, + PythonPackageNamespace +) import pytest import pathlib @pytest.mark.parametrize('python_documentation_generator,expected', [ - (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), - (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), - (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])]), + ( + PythonDocumentationGenerator( + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_1' + ), + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_1/docs' + ) + ), + [] + ), + ( + PythonDocumentationGenerator( + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_2' + ), + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_2/docs' + ) + ), + [ + PythonPackageNamespace( + 'pkg', + [ + PythonModuleNamespace('module') + ] + ) + ] + ), + ( + PythonDocumentationGenerator( + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_3' + ), + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_3/docs' + ) + ), + [ + PythonPackageNamespace( + 'pkg', + [ + PythonModuleNamespace('module'), + PythonPackageNamespace( + 'pkg', + [ + PythonModuleNamespace( + 'module2' + ) + ] + ) + ] + ) + ] + ), ]) -def test_PythonDocumentationGenerator_collect_modules(python_documentation_generator: PythonDocumentationGenerator, expected): +def test_PythonDocumentationGenerator_collect_modules( + python_documentation_generator: PythonDocumentationGenerator, + expected +): assert python_documentation_generator.namespace() == expected @pytest.mark.parametrize('dir', [ d.name - for d in pathlib.Path('tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/').iterdir() + for d in pathlib.Path( + ( + 'tests/unit/core/python/_core/test_PythonDocume' + 'ntationGenerator/test_generate_documentation/' + ) + ).iterdir() ]) def test_generate_documentation(dir, expectdir): - with expectdir('test_PythonDocumentationGenerator/test_generate_documentation/' + dir) as d: + with expectdir( + 'test_PythonDocumentationGenerator/test_generate_documentation/' + dir + ) as d: PythonDocumentationGenerator( pathlib.Path( str( - pathlib.Path('tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/') / + pathlib.Path( + ( + 'tests/unit/core/python/_core/test_PythonDocume' + 'ntationGenerator/test_generate_documentation/' + ) + ) / dir / 'project' ) @@ -42,5 +112,4 @@ def test_generate_documentation(dir, expectdir): pathlib.Path(str(d)), allow_tables = True, allow_html = False - ).generate_documentation( - ) # simplify the tests + ).generate_documentation() # simplify the tests diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case03/project/src/user_management/service.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case03/project/src/user_management/service.py index 67c2f61..755c0d8 100644 --- a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case03/project/src/user_management/service.py +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case03/project/src/user_management/service.py @@ -12,7 +12,13 @@ class UserService: def __init__(self, storage: InMemoryUserStorage): self.storage = storage - def create_user(self, user_id: str, name: str, email: str, age: int | None = None) -> User: + def create_user( + self, + user_id: str, + name: str, + email: str, + age: int | None = None + ) -> User: """ Creates and stores a new user. """ diff --git a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py index ee24c8e..92ba600 100644 --- a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py +++ b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py @@ -17,29 +17,223 @@ from src.jcloud_docsgen.exceptions import InvalidNamespaceError import pathlib import pytest -@pytest.mark.parametrize('module_path,src_directory,expected_exception,expected_exception_msg', [ - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g.h'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g.h.py'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g.py'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.py'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.py'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.py'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.py'), - (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.py'), - (pathlib.Path('tests/unit/core/python/_core/directory.with.more.than.one.dot/a.b.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.py'), -]) -def test_PythonModuleDocumentationGenerator_exceptions(module_path, src_directory, expected_exception, expected_exception_msg): +@pytest.mark.parametrize( + 'module_path,src_directory,expected_exception,expected_exception_msg', + [ + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/fil' + 'es_with_more_than_one_dot/a.b.c' + ) + ), + None, + InvalidNamespaceError, + 'namespace identifier cannot contain more than one dot: a.b.c' + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/file' + 's_with_more_than_one_dot/a.b.c.d' + ) + ), + None, + InvalidNamespaceError, + 'namespace identifier cannot contain more than one dot: a.b.c.d' + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files' + '_with_more_than_one_dot/a.b.c.d.e' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot cont' + 'ain more than one dot: a.b.c.d.e' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files_' + 'with_more_than_one_dot/a.b.c.d.e.f' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot conta' + 'in more than one dot: a.b.c.d.e.f' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files_w' + 'ith_more_than_one_dot/a.b.c.d.e.f.g' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot contai' + 'n more than one dot: a.b.c.d.e.f.g' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files_wi' + 'th_more_than_one_dot/a.b.c.d.e.f.g.h' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot contain' + ' more than one dot: a.b.c.d.e.f.g.h' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files_wit' + 'h_more_than_one_dot/a.b.c.d.e.f.g.h.py' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot contain m' + 'ore than one dot: a.b.c.d.e.f.g.h.py' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files_wi' + 'th_more_than_one_dot/a.b.c.d.e.f.g.py' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot contain ' + 'more than one dot: a.b.c.d.e.f.g.py' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files_w' + 'ith_more_than_one_dot/a.b.c.d.e.f.py' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot contain' + ' more than one dot: a.b.c.d.e.f.py' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files_' + 'with_more_than_one_dot/a.b.c.d.e.py' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot contai' + 'n more than one dot: a.b.c.d.e.py' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/files' + '_with_more_than_one_dot/a.b.c.d.py' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot conta' + 'in more than one dot: a.b.c.d.py' + ) + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/file' + 's_with_more_than_one_dot/a.b.c.py' + ) + ), + None, + InvalidNamespaceError, + ( + 'namespace identifier cannot cont' + 'ain more than one dot: a.b.c.py' + ) + ), + ( + pathlib.Path( + ( + ( + 'tests/unit/core/python/_core/fil' + 'es_with_more_than_one_dot/a.b.py' + ) + ) + ), + None, + InvalidNamespaceError, + 'namespace identifier cannot contain more than one dot: a.b.py' + ), + ( + pathlib.Path( + ( + 'tests/unit/core/python/_core/direc' + 'tory.with.more.than.one.dot/a.b.py' + ) + ), + None, + InvalidNamespaceError, + 'namespace identifier cannot contain more than one dot: a.b.py' + ), + ] +) +def test_PythonModuleDocumentationGenerator_exceptions( + module_path, + src_directory, + expected_exception, + expected_exception_msg +): with pytest.raises(expected_exception) as exc_info: PythonModuleDocumentationGenerator(module_path, src_directory) assert str(exc_info.value) == expected_exception_msg @pytest.mark.parametrize('module_documentation_generator,expected', [ - (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/'), allow_tables = True, allow_html = False), '''# Module `pkg.module` + ( + PythonModuleDocumentationGenerator( + pathlib.Path( + ( + 'tests/unit/core/python/_core/test_pr' + 'oject_dirs/pdir_2/src/pkg/module.py' + ) + ), + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_2/src/' + ), + allow_tables = True, + allow_html = False + ), + '''# Module `pkg.module` A test module. @@ -206,9 +400,22 @@ Barks. ### Docstring -A dog.'''), +A dog.''' + ), - (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = True, allow_html = False), '''# Module `module` + ( + PythonModuleDocumentationGenerator( + pathlib.Path( + ( + 'tests/unit/core/python/_core/test_pr' + 'oject_dirs/pdir_2/src/pkg/module.py' + ) + ), + None, + allow_tables = True, + allow_html = False + ), + '''# Module `module` A test module. @@ -375,9 +582,23 @@ Barks. ### Docstring -A dog.'''), +A dog.''' + ), - (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = True, allow_html = True), '''# Module `module` + ( + PythonModuleDocumentationGenerator( + pathlib.Path( + ( + 'tests/unit/core/python/_core/test_pr' + 'oject_dirs/pdir_2/src/pkg/module.py' + ) + ), + None, + allow_tables = True, + allow_html = True + ), + ( + '''# Module `module` A test module. @@ -411,7 +632,8 @@ Return type: `int | float` -

Docstring

Adds two numbers. +

Docstring

Adds ''' + '''two numbers. :param a: The first number. :type a: int | float @@ -478,7 +700,8 @@ Return type: `str` -
Docstring
Returns a greeting. +
Docstring
''' + '''Returns a greeting. :return: The greeting. :rtype: str
@@ -487,7 +710,8 @@ Return type: `str` -

Docstring

A class representing an animal. +

Docstring

A ''' + '''class representing an animal. :param name: Its name. :type name: str
@@ -528,15 +752,32 @@ Return type: `None` -
Docstring
Barks.
+
Docstring
Barks.''' + '''
-

Docstring

A dog.
'''), +

Docstring

A dog.''' + '
' + ) + ), - (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = False, allow_html = True), '''# Module `module` + ( + PythonModuleDocumentationGenerator( + pathlib.Path( + ( + 'tests/unit/core/python/_core/test_pr' + 'oject_dirs/pdir_2/src/pkg/module.py' + ) + ), + None, + allow_tables = False, + allow_html = True + ), + ( + '''# Module `module` A test module. @@ -585,7 +826,8 @@ Return type: `int | float` -

Docstring

Adds two numbers. +

Docstring

Adds ''' + '''two numbers. :param a: The first number. :type a: int | float @@ -673,7 +915,8 @@ Return type: `str` -
Docstring
Returns a greeting. +
Docstring
''' + '''Returns a greeting. :return: The greeting. :rtype: str
@@ -682,7 +925,8 @@ Return type: `str` -

Docstring

A class representing an animal. +

Docstring

A ''' + '''class representing an animal. :param name: Its name. :type name: str
@@ -729,15 +973,31 @@ Return type: `None` -
Docstring
Barks.
+
Docstring
Barks.''' + '''
-

Docstring

A dog.
'''), +

Docstring

A ''' + 'dog.
' + ) + ), - (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = False, allow_html = False), '''# Module `module` + ( + PythonModuleDocumentationGenerator( + pathlib.Path( + ( + 'tests/unit/core/python/_core/test_pr' + 'oject_dirs/pdir_2/src/pkg/module.py' + ) + ), + None, + allow_tables = False, + allow_html = False + ), + '''# Module `module` A test module. @@ -946,7 +1206,11 @@ Barks. ### Docstring -A dog.'''), +A dog.''' + ), ]) -def test_PythonModuleDocumentationGenerator_exceptions(module_documentation_generator, expected): +def test_PythonModuleDocumentationGenerator_exceptions( + module_documentation_generator, + expected +): assert module_documentation_generator.generate_documentation() == expected diff --git a/tests/unit/core/python/_core/test__collect_definitions.py b/tests/unit/core/python/_core/test__collect_definitions.py index cdfadcf..b61deb8 100644 --- a/tests/unit/core/python/_core/test__collect_definitions.py +++ b/tests/unit/core/python/_core/test__collect_definitions.py @@ -13,8 +13,14 @@ # limitations under the License. from src.jcloud_docsgen.core.python._core import _collect_definitions -from src.jcloud_docsgen.core.python.definitions import PythonClassDefinition, PythonFunctionDefinition -from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind +from src.jcloud_docsgen.core.python.definitions import ( + PythonClassDefinition, + PythonFunctionDefinition +) +from src.jcloud_docsgen.core.python.arguments import ( + PythonFunctionArgument, + PythonArgumentKind +) from src.jcloud_docsgen.core.python.docstrings import PythonDocstring import pytest import ast @@ -33,13 +39,33 @@ def module(source_code: str) -> ast.Module: return ast.parse(source_code) @pytest.mark.parametrize('module,expected', [ - (module('def add(a, b):\n\treturn a + b'), [ - PythonFunctionDefinition('add', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - ], None, None, []) - ]), - (module('''def add(a: int | float, b: int | float) -> int | float: + ( + module('def add(a, b):\n\treturn a + b'), + [ + PythonFunctionDefinition( + 'add', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + ], + None, + None, + [] + ) + ] + ), + ( + module('''def add(a: int | float, b: int | float) -> int | float: """Adds two numbers.""" return a + b @@ -62,26 +88,94 @@ class Something: Returns the value. """ return self._value -'''), [ - PythonFunctionDefinition('add', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int | float'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int | float'), - ], 'int | float', PythonDocstring('Adds two numbers.'), []), - PythonClassDefinition('Something', [], PythonDocstring('A class.'), [], [ - PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('value', PythonArgumentKind.NORMAL, '42', 'typing.Any'), - ], 'None', None, []), - PythonFunctionDefinition('value', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - ], 'typing.Any', PythonDocstring('The value.'), ['property']), - PythonFunctionDefinition('get_value', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - ], 'typing.Any', PythonDocstring('Returns the value.'), []), - ]) - ]), - (module('''from src.jcloud_docsgen.core.python import PythonDocumentationGenerator -from src.jcloud_docsgen.core.python.namespaces import PythonModuleNamespace, PythonPackageNamespace +'''), + [ + PythonFunctionDefinition( + 'add', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int | float' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'int | float' + ), + ], + 'int | float', + PythonDocstring('Adds two numbers.'), + [] + ), + PythonClassDefinition( + 'Something', + [], + PythonDocstring('A class.'), + [], + [ + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.NORMAL, + '42', + 'typing.Any' + ), + ], + 'None', + None, + [] + ), + PythonFunctionDefinition( + 'value', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + ], + 'typing.Any', + PythonDocstring('The value.'), + ['property'] + ), + PythonFunctionDefinition( + 'get_value', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + ], + 'typing.Any', + PythonDocstring('Returns the value.'), + [] + ), + ] + ) + ] + ), + ( + module('''from src.jcloud_docsgen.core.python import ( + PythonDocumentationGenerator +) +from src.jcloud_docsgen.core.python.namespaces import ( + PythonModuleNamespace, + PythonPackageNamespace +) import pytest @pytest.mark.parametrize('project_directory,docs_directory', [ @@ -94,45 +188,186 @@ import pytest (1, pathlib.Path('tests')), (None, pathlib.Path('tests')), ]) -def test_PythonDocumentationGenerator_type_exceptions(project_directory, docs_directory): +def test_PythonDocumentationGenerator_type_exceptions( + project_directory, + docs_directory +): with pytest.raises(TypeError): PythonDocumentationGenerator(project_directory, docs_directory) @pytest.mark.parametrize('python_documentation_generator,expected', [ - (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), - (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), - (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])]), + (PythonDocumentationGenerator( + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_1' + ), + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_1/docs' + ) + ), []), + (PythonDocumentationGenerator( + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_2' + ), + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_2/docs' + ) + ), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), + (PythonDocumentationGenerator( + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_3' + ), + pathlib.Path( + 'tests/unit/core/python/_core/test_project_dirs/pdir_3/docs' + ) + ), [ + PythonPackageNamespace( + 'pkg', + [ + PythonModuleNamespace('module'), + PythonPackageNamespace('pkg', + [PythonModuleNamespace('module2')] + ) + ] + ) + ]) ]) -def test_PythonDocumentationGenerator_collect_modules(python_documentation_generator: PythonDocumentationGenerator, expected): - assert python_documentation_generator.namespace() == expected'''), [ - PythonFunctionDefinition('test_PythonDocumentationGenerator_type_exceptions', [ - PythonFunctionArgument('project_directory', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('docs_directory', PythonArgumentKind.NORMAL, None, None), - ], None, None, ['''pytest.mark.parametrize('project_directory,docs_directory', [(1, 1), (1, None), (42, None), (pathlib.Path('tests'), 1), (pathlib.Path('tests/'), 1), (1, pathlib.Path('tests/')), (1, pathlib.Path('tests')), (None, pathlib.Path('tests'))])''']), - PythonFunctionDefinition('test_PythonDocumentationGenerator_collect_modules', [ - PythonFunctionArgument('python_documentation_generator', PythonArgumentKind.NORMAL, None, 'PythonDocumentationGenerator'), - PythonFunctionArgument('expected', PythonArgumentKind.NORMAL, None, None) - ], None, None, ['''pytest.mark.parametrize('python_documentation_generator,expected', [(PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])])])''']) - ]), - (module('''class C1: +def test_PythonDocumentationGenerator_collect_modules( + python_documentation_generator: PythonDocumentationGenerator, + expected +): + assert python_documentation_generator.namespace() == expected'''), + [ + PythonFunctionDefinition( + 'test_PythonDocumentationGenerator_type_exceptions', + [ + PythonFunctionArgument( + 'project_directory', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'docs_directory', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + None, + None, + [ + ( + '''pytest.mark.parametrize('project_directo''' + '''ry,docs_directory', [(1, 1), (1, None), ''' + '''(42, None), (pathlib.Path('tests'), 1), ''' + '''(pathlib.Path('tests/'), 1), (1, pathlib''' + '''.Path('tests/')), (1, pathlib.Path('test''' + '''s')), (None, pathlib.Path('tests'))])''' + ) + ] + ), + PythonFunctionDefinition( + 'test_PythonDocumentationGenerator_collect_modules', + [ + PythonFunctionArgument( + 'python_documentation_generator', + PythonArgumentKind.NORMAL, + None, + 'PythonDocumentationGenerator' + ), + PythonFunctionArgument( + 'expected', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + None, + None, + [( + '''pytest.mark.parametrize('python_documentation_gener''' + '''ator,expected', [(PythonDocumentationGenerator(path''' + '''lib.Path('tests/unit/core/python/_core/test_project''' + '''_dirs/pdir_1'), pathlib.Path('tests/unit/core/pytho''' + '''n/_core/test_project_dirs/pdir_1/docs')), []), (Pyt''' + '''honDocumentationGenerator(pathlib.Path('tests/unit/''' + '''core/python/_core/test_project_dirs/pdir_2'), pathl''' + '''ib.Path('tests/unit/core/python/_core/test_project_''' + '''dirs/pdir_2/docs')), [PythonPackageNamespace('pkg',''' + ''' [PythonModuleNamespace('module')])]), (PythonDocum''' + '''entationGenerator(pathlib.Path('tests/unit/core/pyt''' + '''hon/_core/test_project_dirs/pdir_3'), pathlib.Path(''' + ''''tests/unit/core/python/_core/test_project_dirs/pdi''' + '''r_3/docs')), [PythonPackageNamespace('pkg', [Python''' + '''ModuleNamespace(\'module'), PythonPackageNamespace(''' + '''\'pkg\', [PythonModuleNamespace(\'module2')])])])])''' + )] + ) + ] + ), + ( + module( + '''class C1: class C2: class C3: def function(self, value: bool, /, arg) -> bool: def should_be_not_in_the_result(arg: int) -> object: pass - return not value'''), [ - PythonClassDefinition('C1', [], None, [], [ - PythonClassDefinition('C2', [], None, [], [ - PythonClassDefinition('C3', [], None, [], [ - PythonFunctionDefinition('function', [ - PythonFunctionArgument('self', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('value', PythonArgumentKind.POSITIONAL_ONLY, None, 'bool'), - PythonFunctionArgument('arg', PythonArgumentKind.NORMAL, None, None), - ], 'bool', None, []) - ]) - ]) - ]) - ]) + return not value''' + ), + [ + PythonClassDefinition( + 'C1', + [], + None, + [], + [ + PythonClassDefinition( + 'C2', + [], + None, + [], + [ + PythonClassDefinition( + 'C3', + [], + None, + [], + [ + PythonFunctionDefinition( + 'function', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'bool' + ), + PythonFunctionArgument( + 'arg', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + 'bool', + None, + [] + ) + ] + ) + ] + ) + ] + ) + ] + ) ]) def test___collect_definitions(module, expected): assert list(_collect_definitions(module)) == expected diff --git a/tests/unit/core/python/_core/test__function_params_info.py b/tests/unit/core/python/_core/test__function_params_info.py index 66ee9ed..dc52e82 100644 --- a/tests/unit/core/python/_core/test__function_params_info.py +++ b/tests/unit/core/python/_core/test__function_params_info.py @@ -12,12 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.jcloud_docsgen.core.python._core import _function_params_info, _FunctionParamInfo -from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind +from src.jcloud_docsgen.core.python._core import ( + _function_params_info, + _FunctionParamInfo +) +from src.jcloud_docsgen.core.python.arguments import ( + PythonFunctionArgument, + PythonArgumentKind +) import pytest import docstring_parser -def docstring_args_list(docstring: str) -> list[docstring_parser.DocstringParam]: +def docstring_args_list( + docstring: str +) -> list[docstring_parser.DocstringParam]: ''' Returns the parameter list of a docstring. @@ -101,4 +109,7 @@ def docstring_args_list(docstring: str) -> list[docstring_parser.DocstringParam] ([], '', {}), ]) def test__function_params_types(arg_list, docstring, expected): - assert _function_params_info(arg_list, docstring_args_list(docstring)) == expected + assert _function_params_info( + arg_list, + docstring_args_list(docstring) + ) == expected diff --git a/tests/unit/core/python/_core/test__markdown_table.py b/tests/unit/core/python/_core/test__markdown_table.py index 3cb4249..1b41de1 100644 --- a/tests/unit/core/python/_core/test__markdown_table.py +++ b/tests/unit/core/python/_core/test__markdown_table.py @@ -15,79 +15,406 @@ from src.jcloud_docsgen.core.python._core import _markdown_table import pytest -@pytest.mark.parametrize('header,body,allow_markdown_table,force_row_lengths,one_line_values,missing_value,expected', [ - ([], [], False, False, True, '--', ''), - ([], [('a',)], False, False, True, '--', ''), - (['a'], [], False, False, True, '--', ''), - (['a'], [(1,)], True, False, True, '--', '''| a | +@pytest.mark.parametrize( + ( + 'header,body,allow_markdown_table,force_row_le' + 'ngths,one_line_values,missing_value,expected' + ), + [ + ( + [], + [], + False, + False, + True, + '--', + '' + ), + ( + [], + [('a',)], + False, + False, + True, + '--', + '' + ), + ( + ['a'], + [], + False, + False, + True, + '--', + '' + ), + ( + ['a'], + [(1,)], + True, + False, + True, + '--', + '''| a | | --- | -| 1 |'''), - (['a'], [(1, 2)], True, False, True, '--', '''| a | +| 1 |''' + ), + ( + ['a'], + [(1, 2)], + True, + False, + True, + '--', + '''| a | | --- | -| 1 |'''), - (['a', 'b'], [(1, 2)], True, False, True, '--', '''| a | b | +| 1 |''' + ), + ( + ['a', 'b'], + [(1, 2)], + True, + False, + True, + '--', + '''| a | b | | --- | --- | -| 1 | 2 |'''), - (['a', 'b'], [(1,)], True, False, True, '--', '''| a | b | +| 1 | 2 |''' + ), + ( + ['a', 'b'], + [(1,)], + True, + False, + True, + '--', + '''| a | b | | --- | --- | -| 1 | -- |'''), - (['a', 'b'], [()], True, False, True, '--', '''| a | b | +| 1 | -- |''' + ), + ( + ['a', 'b'], + [()], + True, + False, + True, + '--', + '''| a | b | | --- | --- | -| -- | -- |'''), - (['a', 'b'], [(1, 2), (3, 4)], True, False, True, '--', '''| a | b | +| -- | -- |''' + ), + ( + ['a', 'b'], + [(1, 2), (3, 4)], + True, + False, + True, + '--', + '''| a | b | | --- | --- | | 1 | 2 | -| 3 | 4 |'''), - (['a'], [('line1\nline2',)], True, False, True, '--', '''| a | +| 3 | 4 |''' + ), + ( + ['a'], + [('line1\nline2',)], + True, + False, + True, + '--', + '''| a | | --- | -| line1 line2 |'''), - (['a'], [('line1\nline2',)], True, False, False, '--', '''| a | +| line1 line2 |''' + ), + ( + ['a'], + [('line1\nline2',)], + True, + False, + False, + '--', + '''| a | | --- | -| line1\nline2 |'''), +| line1\nline2 |''' + ), - ([], [], False, False, True, '--', ''), - ([], [('a',)], False, False, True, '--', ''), - (['a'], [], False, False, True, '--', ''), - (['a'], [(1,)], False, False, True, '--', '''- a: 1'''), - (['a'], [(1, 2)], False, False, True, '--', '''- a: 1'''), - (['a', 'b'], [(1, 2)], False, False, True, '--', '''- a: 1 + ( + [], + [], + False, + False, + True, + '--', + '' + ), + ( + [], + [('a',)], + False, + False, + True, + '--', + '' + ), + ( + ['a'], + [], + False, + False, + True, + '--', + '' + ), + ( + ['a'], + [(1,)], + False, + False, + True, + '--', + '- a: 1' + ), + ( + ['a'], + [(1, 2)], + False, + False, + True, + '--', + '- a: 1' + ), + ( + ['a', 'b'], + [(1, 2)], + False, + False, + True, + '--', + '''- a: 1 - b: 2'''), - (['a', 'b'], [(1,)], False, False, True, '--', '''- a: 1 + b: 2''' + ), + ( + ['a', 'b'], + [(1,)], + False, + False, + True, + '--', + '''- a: 1 - b: --'''), - (['a', 'b'], [()], False, False, True, '--', '''- a: -- + b: --''' + ), + ( + ['a', 'b'], + [()], + False, + False, + True, + '--', + '''- a: -- - b: --'''), - (['a', 'b'], [(1, 2), (3, 4)], False, False, True, '--', '''- a: 1 + b: --''' + ), + ( + ['a', 'b'], + [(1, 2), (3, 4)], + False, + False, + True, + '--', + '''- a: 1 b: 2 - a: 3 - b: 4'''), - (['a'], [('line1\nline2',)], False, False, True, '--', '- a: line1 line2'), - (['a'], [('line1\nline2',)], False, False, False, '--', '- a: line1\nline2'), -]) -def test__markdown_table(header, body, allow_markdown_table, force_row_lengths, one_line_values, missing_value, expected): - assert _markdown_table(header, body, allow_markdown_table = allow_markdown_table, force_row_lengths = force_row_lengths, one_line_values = one_line_values, missing_value = missing_value) == expected + b: 4''' + ), + ( + ['a'], + [('line1\nline2',)], + False, + False, + True, + '--', + '- a: line1 line2' + ), + ( + ['a'], + [('line1\nline2',)], + False, + False, + False, + '--', + '- a: line1\nline2' + ) + ] +) +def test__markdown_table( + header, + body, + allow_markdown_table, + force_row_lengths, + one_line_values, + missing_value, + expected +): + assert _markdown_table( + header, + body, + allow_markdown_table = allow_markdown_table, + force_row_lengths = force_row_lengths, + one_line_values = one_line_values, + missing_value = missing_value + ) == expected -@pytest.mark.parametrize('header,body,allow_markdown_table,force_row_lengths,one_line_values,missing_value,expected_exception,expected_exception_msg', [ - (['a'], [(1, 2)], True, True, True, '--', ValueError, 'expected 1 value, got 2'), - (['a'], [(1, 2, 3)], True, True, True, '--', ValueError, 'expected 1 value, got 3'), - (['a'], [()], True, True, True, '--', ValueError, 'expected 1 value, got 0'), - (['a', 'b'], [()], True, True, True, '--', ValueError, 'expected 2 values, got 0'), - (['a', 'b'], [(1,)], True, True, True, '--', ValueError, 'expected 2 values, got 1'), - (['a', 'b'], [(1, 2, 3)], True, True, True, '--', ValueError, 'expected 2 values, got 3'), +@pytest.mark.parametrize( + ( + 'header,body,allow_markdown_table,force_row_lengths,one_line_v' + 'alues,missing_value,expected_exception,expected_exception_msg' + ), + [ + ( + ['a'], + [(1, 2)], + True, + True, + True, + '--', + ValueError, + 'expected 1 value, got 2' + ), + ( + ['a'], + [(1, 2, 3)], + True, + True, + True, + '--', + ValueError, + 'expected 1 value, got 3' + ), + ( + ['a'], + [()], + True, + True, + True, + '--', + ValueError, + 'expected 1 value, got 0' + ), + ( + ['a', 'b'], + [()], + True, + True, + True, + '--', + ValueError, + 'expected 2 values, got 0' + ), + ( + ['a', 'b'], + [(1,)], + True, + True, + True, + '--', + ValueError, + 'expected 2 values, got 1' + ), + ( + ['a', 'b'], + [(1, 2, 3)], + True, + True, + True, + '--', + ValueError, + 'expected 2 values, got 3' + ), - (['a'], [(1, 2)], False, True, True, '--', ValueError, 'expected 1 value, got 2'), - (['a'], [(1, 2, 3)], False, True, True, '--', ValueError, 'expected 1 value, got 3'), - (['a'], [()], False, True, True, '--', ValueError, 'expected 1 value, got 0'), - (['a', 'b'], [()], False, True, True, '--', ValueError, 'expected 2 values, got 0'), - (['a', 'b'], [(1,)], False, True, True, '--', ValueError, 'expected 2 values, got 1'), - (['a', 'b'], [(1, 2, 3)], False, True, True, '--', ValueError, 'expected 2 values, got 3'), -]) -def test__markdown_table_exceptions(header, body, allow_markdown_table, force_row_lengths, one_line_values, missing_value, expected_exception, expected_exception_msg): + ( + ['a'], + [(1, 2)], + False, + True, + True, + '--', + ValueError, + 'expected 1 value, got 2' + ), + ( + ['a'], + [(1, 2, 3)], + False, + True, + True, + '--', + ValueError, + 'expected 1 value, got 3' + ), + ( + ['a'], + [()], + False, + True, + True, + '--', + ValueError, + 'expected 1 value, got 0' + ), + ( + ['a', 'b'], + [()], + False, + True, + True, + '--', + ValueError, + 'expected 2 values, got 0' + ), + ( + ['a', 'b'], + [(1,)], + False, + True, + True, + '--', + ValueError, + 'expected 2 values, got 1' + ), + ( + ['a', 'b'], + [(1, 2, 3)], + False, + True, + True, + '--', + ValueError, + 'expected 2 values, got 3' + ) + ] +) +def test__markdown_table_exceptions( + header, + body, + allow_markdown_table, + force_row_lengths, + one_line_values, + missing_value, + expected_exception, + expected_exception_msg +): with pytest.raises(expected_exception) as exc_info: - _markdown_table(header, body, allow_markdown_table = allow_markdown_table, force_row_lengths = force_row_lengths, one_line_values = one_line_values, missing_value = missing_value) + _markdown_table( + header, + body, + allow_markdown_table = allow_markdown_table, + force_row_lengths = force_row_lengths, + one_line_values = one_line_values, + missing_value = missing_value + ) assert str(exc_info.value) == expected_exception_msg diff --git a/tests/unit/core/python/_core/test__signature_argument_list.py b/tests/unit/core/python/_core/test__signature_argument_list.py index 7c1f49c..7448647 100644 --- a/tests/unit/core/python/_core/test__signature_argument_list.py +++ b/tests/unit/core/python/_core/test__signature_argument_list.py @@ -13,84 +13,271 @@ # limitations under the License. from src.jcloud_docsgen.core.python._core import _signature_argument_list -from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind +from src.jcloud_docsgen.core.python.arguments import ( + PythonFunctionArgument, + PythonArgumentKind +) import pytest @pytest.mark.parametrize('argument_list,expected', [ ([ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None) + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ) ], ['a']), ([ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None) + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ) ], ['a', '/']), ([ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ) ], ['a', '/', 'b']), ([ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ) ], ['a', '/', 'b', '*', 'c']), ([ - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ) ], ['b', '*', 'c']), ([ - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ) ], ['*', 'c']), ([ - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ) ], ['b', '/', '*', 'c']), ([ - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ) ], ['*args']), ([ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ) ], ['a', '*args']), ([ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), + PythonFunctionArgument( + 'a', PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ) ], ['a', '/', '*args']), ([ - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('a', PythonArgumentKind.KEYWORD_ONLY, None, None), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'a', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ) ], ['*args', 'a']), ([ - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) ], ['*args', '**kwargs']), ([ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) ], ['a', '/', '*args', '**kwargs']), ([ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) ], ['a', '*args', '**kwargs']), ([ - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('a', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'a', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) ], ['*args', 'a', '**kwargs']), ([ - PythonFunctionArgument('a', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) ], ['*', 'a', '**kwargs']), ([ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) ], ['a', '**kwargs']), ([ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) ], ['a', '/', '**kwargs']), ([ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '1', 'int'), + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '1', + 'int' + ), ], ['a: int = 1']), ]) def test__signature_argument_list(argument_list, expected): diff --git a/tests/unit/core/python/arguments/test_PythonASTArgumentsListParser.py b/tests/unit/core/python/arguments/test_PythonASTArgumentsListParser.py index 2345b97..553f052 100644 --- a/tests/unit/core/python/arguments/test_PythonASTArgumentsListParser.py +++ b/tests/unit/core/python/arguments/test_PythonASTArgumentsListParser.py @@ -12,7 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.jcloud_docsgen.core.python.arguments import PythonASTArgumentsListParser, PythonFunctionArgument, PythonArgumentKind +from src.jcloud_docsgen.core.python.arguments import ( + PythonASTArgumentsListParser, + PythonFunctionArgument, + PythonArgumentKind +) import pytest import ast @@ -30,186 +34,911 @@ def argument_list(argument_list: str) -> ast.arguments: return ast.parse('def func(' + argument_list + '): ...').body[0].args @pytest.mark.parametrize('ast_arguments_list,expected', [ - (argument_list('a'), [PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None)]), - (argument_list('a: str'), [PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str')]), - (argument_list('a: str | int'), [PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str | int')]), - (argument_list('a: str, b'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None) - ]), - (argument_list('a: str, b: int'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int') - ]), - (argument_list('a, b: int'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int') - ]), - (argument_list('a, b, *args'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None) - ]), - (argument_list('a: int, b, *args'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None) - ]), - (argument_list('a, b: str, *args'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'str'), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None) - ]), - (argument_list('a: int, b: str, *args'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'str'), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None) - ]), - (argument_list('a: int, b: str, *args: float'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'str'), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'float') - ]), - (argument_list('a: int, b, *args: float'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'float') - ]), - (argument_list('a: int, *args: float'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'float') - ]), - (argument_list('a: int, /, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None) - ]), - (argument_list('a: int, **kwargs: str'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'str') - ]), - (argument_list('a: int, /, b, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None) - ]), - (argument_list('a: int, /, b'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - ]), - (argument_list('a: int, /, b, *, c'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), - ]), - (argument_list('a, b, *, c'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), - ]), - (argument_list('a, b, *args, c'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), - ]), - (argument_list('a, b, *args, c, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a, b, /, *, c, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a, b, /, c, *, d, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('c', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a: str, b: int, /, c: float, d: list, *, e: tuple, f: dict, **kwargs: set'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'str'), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('c', PythonArgumentKind.NORMAL, None, 'float'), - PythonFunctionArgument('d', PythonArgumentKind.NORMAL, None, 'list'), - PythonFunctionArgument('e', PythonArgumentKind.KEYWORD_ONLY, None, 'tuple'), - PythonFunctionArgument('f', PythonArgumentKind.KEYWORD_ONLY, None, 'dict'), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'set'), - ]), - (argument_list('a: str, b: int, /, c: float, d: list, *args: bytes, e: tuple, f: dict, **kwargs: set'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'str'), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('c', PythonArgumentKind.NORMAL, None, 'float'), - PythonFunctionArgument('d', PythonArgumentKind.NORMAL, None, 'list'), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'bytes'), - PythonFunctionArgument('e', PythonArgumentKind.KEYWORD_ONLY, None, 'tuple'), - PythonFunctionArgument('f', PythonArgumentKind.KEYWORD_ONLY, None, 'dict'), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'set'), - ]), - (argument_list('a: str, b: int, /, *args: bytes, c: tuple, d: dict, **kwargs: set'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'str'), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'bytes'), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, 'tuple'), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, None, 'dict'), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'set'), - ]), - (argument_list('a = 1, b = \'a\', /, *args, c = None, d = True, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, '1', None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, '\'a\'', None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, 'None', None), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, 'True', None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a, b = \'a\', /, *args, c = None, d = True, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, '\'a\'', None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, 'None', None), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, 'True', None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a, b , /, *args, c = None, d = True, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, 'None', None), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, 'True', None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a, b , /, *args, c, d = True, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, 'True', None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a, b , /, *args, c, d, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a, b , /, *args, c = None, d, **kwargs'), [ - PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None), - PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None), - PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, 'None', None), - PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, None, None), - PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None), - ]), - (argument_list('a = 1'), [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '1', None) - ]), + ( + argument_list('a'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ) + ] + ), + ( + argument_list('a: str'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'str' + ) + ] + ), + ( + argument_list('a: str | int'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'str | int' + ) + ] + ), + ( + argument_list('a: str, b'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'str' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ) + ] + ), + ( + argument_list('a: str, b: int'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'str' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'int' + ) + ] + ), + ( + argument_list('a, b: int'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'int' + ) + ] + ), + ( + argument_list('a, b, *args'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ) + ] + ), + ( + argument_list('a: int, b, *args'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ) + ] + ), + ( + argument_list('a, b: str, *args'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'str' + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ) + ] + ), + ( + argument_list('a: int, b: str, *args'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'str' + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ) + ] + ), + ( + argument_list('a: int, b: str, *args: float'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'str' + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + 'float' + ) + ] + ), + ( + argument_list('a: int, b, *args: float'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + 'float' + ) + ] + ), + ( + argument_list('a: int, *args: float'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + 'float' + ) + ] + ), + ( + argument_list('a: int, /, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) + ] + ), + ( + argument_list('a: int, **kwargs: str'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + 'str' + ) + ] + ), + ( + argument_list('a: int, /, b, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ) + ] + ), + ( + argument_list('a: int, /, b'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + ] + ), + ( + argument_list('a: int, /, b, *, c'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + ] + ), + ( + argument_list('a, b, *, c'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + ] + ), + ( + argument_list('a, b, *args, c'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + ] + ), + ( + argument_list('a, b, *args, c, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a, b, /, *, c, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a, b, /, c, *, d, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list( + ( + 'a: str, b: int, /, c: float, d: list,' + ' *, e: tuple, f: dict, **kwargs: set' + ) + ), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'str' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.NORMAL, + None, + 'float' + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.NORMAL, + None, + 'list' + ), + PythonFunctionArgument( + 'e', + PythonArgumentKind.KEYWORD_ONLY, + None, + 'tuple' + ), + PythonFunctionArgument( + 'f', + PythonArgumentKind.KEYWORD_ONLY, + None, + 'dict' + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + 'set' + ), + ] + ), + ( + argument_list( + ( + 'a: str, b: int, /, c: float, d: list, *arg' + 's: bytes, e: tuple, f: dict, **kwargs: set' + ) + ), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'str' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.NORMAL, + None, + 'float' + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.NORMAL, + None, + 'list' + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + 'bytes' + ), + PythonFunctionArgument( + 'e', + PythonArgumentKind.KEYWORD_ONLY, + None, + 'tuple' + ), + PythonFunctionArgument( + 'f', + PythonArgumentKind.KEYWORD_ONLY, + None, + 'dict' + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + 'set' + ), + ] + ), + ( + argument_list( + ( + 'a: str, b: int, /, *args: bytes, ' + 'c: tuple, d: dict, **kwargs: set' + ) + ), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'str' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + 'bytes' + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + 'tuple' + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + None, + 'dict' + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + 'set' + ), + ] + ), + ( + argument_list( + 'a = 1, b = \'a\', /, *args, c = None, d = True, **kwargs' + ), [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + '1', + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + '\'a\'', + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + 'None', + None + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + 'True', + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a, b = \'a\', /, *args, c = None, d = True, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + '\'a\'', + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + 'None', + None + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + 'True', + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a, b , /, *args, c = None, d = True, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + 'None', + None + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + 'True', + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a, b , /, *args, c, d = True, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + 'True', + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a, b , /, *args, c, d, **kwargs'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a, b , /, *args, c = None, d, **kwargs'), [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + PythonFunctionArgument( + 'args', + PythonArgumentKind.VARARG, + None, + None + ), + PythonFunctionArgument( + 'c', + PythonArgumentKind.KEYWORD_ONLY, + 'None', + None + ), + PythonFunctionArgument( + 'd', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + PythonFunctionArgument( + 'kwargs', + PythonArgumentKind.KWARGS, + None, + None + ), + ] + ), + ( + argument_list('a = 1'), + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '1', + None + ) + ] + ), ]) -def test_PythonASTArgumentsListParser_to_argument_list(ast_arguments_list, expected): - assert PythonASTArgumentsListParser(ast_arguments_list).to_argument_list() == expected +def test_PythonASTArgumentsListParser_to_argument_list( + ast_arguments_list, + expected +): + assert PythonASTArgumentsListParser( + ast_arguments_list + ).to_argument_list() == expected diff --git a/tests/unit/core/python/arguments/test_PythonFunctionArgument.py b/tests/unit/core/python/arguments/test_PythonFunctionArgument.py index 0b45d5e..2e30b25 100644 --- a/tests/unit/core/python/arguments/test_PythonFunctionArgument.py +++ b/tests/unit/core/python/arguments/test_PythonFunctionArgument.py @@ -12,56 +12,374 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind -from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError, InvalidPythonAnnotationError, PythonArgumentStructureError +from src.jcloud_docsgen.core.python.arguments import ( + PythonFunctionArgument, + PythonArgumentKind +) +from src.jcloud_docsgen.exceptions import ( + InvalidPythonIdentifierError, + InvalidPythonAnnotationError, + PythonArgumentStructureError +) import pytest -@pytest.mark.parametrize('name,kind,default,annotation,expected_exception,expected_exception_msg', [ - (None, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'NoneType\''), - (1, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'int\''), - (42, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'int\''), - (4.2, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'float\''), - (4.2, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'float\''), - ('a', PythonArgumentKind.NORMAL, 1, None, TypeError, 'expected either \'str\' or \'NoneType\', got \'int\''), - ('a', PythonArgumentKind.NORMAL, None, 1, TypeError, 'expected either \'str\' or \'NoneType\', got \'int\''), - ('a', 0, None, None, TypeError, 'expected \'PythonArgumentKind\', got \'int\''), - ('a', PythonArgumentKind.NORMAL, None, '', InvalidPythonAnnotationError, 'invalid annotation'), - ('', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier'), - ('.', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: .'), - ('#', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: #'), - ('1a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: 1a'), - ('1_', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: 1_'), - ('1,', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: 1,'), - (',', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ,'), - (':', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: :'), - ('-', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: -'), - (';', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ;'), - ('|', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: |'), - ('<', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: <'), - ('>', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: >'), - ('a,', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a,'), - (',a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ,a'), - ('a:', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a:'), - (':a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: :a'), - ('a-', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a-'), - ('-a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: -a'), - ('a;', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a;'), - (';a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ;a'), - ('a|', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a|'), - ('|a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: |a'), - ('a<', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a<'), - ('', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a>'), - ('>a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: >a'), - ('class', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: class'), - ('def', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: def'), - ('lambda', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: lambda'), - ('None', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: None'), - ('True', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: True'), - ('a', PythonArgumentKind.VARARG, '1', None, PythonArgumentStructureError, '* or ** arguments cannot have default values: *a = 1'), - ('a', PythonArgumentKind.KWARGS, '1', None, PythonArgumentStructureError, '* or ** arguments cannot have default values: **a = 1'), -]) -def test_PythonFunctionArgument_exceptions(name, kind, default, annotation, expected_exception, expected_exception_msg): +@pytest.mark.parametrize( + 'name,kind,default,annotation,expected_exception,expected_exception_msg', + [ + ( + None, + PythonArgumentKind.NORMAL, + None, + None, + TypeError, + 'expected \'str\', got \'NoneType\'' + ), + ( + 1, + PythonArgumentKind.NORMAL, + None, + None, + TypeError, + 'expected \'str\', got \'int\'' + ), + ( + 42, + PythonArgumentKind.NORMAL, + None, + None, + TypeError, + 'expected \'str\', got \'int\'' + ), + ( + 4.2, + PythonArgumentKind.NORMAL, + None, + None, + TypeError, + 'expected \'str\', got \'float\'' + ), + ( + 4.2, + PythonArgumentKind.NORMAL, + None, + None, + TypeError, + 'expected \'str\', got \'float\'' + ), + ( + 'a', + PythonArgumentKind.NORMAL, + 1, + None, + TypeError, + 'expected either \'str\' or \'NoneType\', got \'int\'' + ), + ( + 'a', + PythonArgumentKind.NORMAL, + None, + 1, + TypeError, + 'expected either \'str\' or \'NoneType\', got \'int\'' + ), + ( + 'a', + 0, + None, + None, + TypeError, + 'expected \'PythonArgumentKind\', got \'int\'' + ), + ( + 'a', + PythonArgumentKind.NORMAL, + None, + '', + InvalidPythonAnnotationError, + 'invalid annotation' + ), + ( + '', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier' + ), + ( + '.', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: .' + ), + ( + '#', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: #' + ), + ( + '1a', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: 1a' + ), + ( + '1_', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: 1_' + ), + ( + '1,', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: 1,' + ), + ( + ',', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: ,' + ), + ( + ':', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: :' + ), + ( + '-', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: -' + ), + ( + ';', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: ;' + ), + ( + '|', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: |' + ), + ( + '<', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: <' + ), + ( + '>', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: >' + ), + ( + 'a,', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: a,' + ), + ( + ',a', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: ,a' + ), + ( + 'a:', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: a:' + ), + ( + ':a', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: :a' + ), + ( + 'a-', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: a-' + ), + ( + '-a', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: -a' + ), + ( + 'a;', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: a;' + ), + ( + ';a', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: ;a' + ), + ( + 'a|', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: a|' + ), + ( + '|a', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: |a' + ), + ( + 'a<', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: a<' + ), + ( + '', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: a>' + ), + ( + '>a', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: >a' + ), + ( + 'class', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: class' + ), + ( + 'def', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: def' + ), + ( + 'lambda', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: lambda' + ), + ( + 'None', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: None' + ), + ( + 'True', + PythonArgumentKind.NORMAL, + None, + None, + InvalidPythonIdentifierError, + 'invalid identifier: True' + ), + ( + 'a', + PythonArgumentKind.VARARG, + '1', + None, + PythonArgumentStructureError, + '* or ** arguments cannot have default values: *a = 1' + ), + ( + 'a', + PythonArgumentKind.KWARGS, + '1', + None, + PythonArgumentStructureError, + '* or ** arguments cannot have default values: **a = 1' + ), + ] +) +def test_PythonFunctionArgument_exceptions( + name, + kind, + default, + annotation, + expected_exception, + expected_exception_msg +): with pytest.raises(expected_exception) as exc_info: PythonFunctionArgument(name, kind, default, annotation) assert str(exc_info.value) == expected_exception_msg @@ -136,22 +454,150 @@ def test_PythonFunctionArgument_asterik(name, kind, annotation): PythonFunctionArgument(name, kind, None, annotation) @pytest.mark.parametrize('argument,expected', [ - (PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None), 'a'), - (PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '1', None), 'a = 1'), - (PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), 'a: int'), - (PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '1', 'int'), 'a: int = 1'), - (PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None), 'a'), - (PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, '1', None), 'a = 1'), - (PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'), 'a: int'), - (PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, '1', 'int'), 'a: int = 1'), - (PythonFunctionArgument('a', PythonArgumentKind.KEYWORD_ONLY, None, None), 'a'), - (PythonFunctionArgument('a', PythonArgumentKind.KEYWORD_ONLY, '1', None), 'a = 1'), - (PythonFunctionArgument('a', PythonArgumentKind.KEYWORD_ONLY, None, 'int'), 'a: int'), - (PythonFunctionArgument('a', PythonArgumentKind.KEYWORD_ONLY, '1', 'int'), 'a: int = 1'), - (PythonFunctionArgument('a', PythonArgumentKind.VARARG, None, None), '*a'), - (PythonFunctionArgument('a', PythonArgumentKind.VARARG, None, 'int'), '*a: int'), - (PythonFunctionArgument('a', PythonArgumentKind.KWARGS, None, None), '**a'), - (PythonFunctionArgument('a', PythonArgumentKind.KWARGS, None, 'int'), '**a: int'), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + None + ), + 'a' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '1', + None + ), + 'a = 1' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + 'a: int' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + '1', + 'int' + ), + 'a: int = 1' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + None + ), + 'a' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + '1', + None + ), + 'a = 1' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + None, + 'int' + ), + 'a: int' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.POSITIONAL_ONLY, + '1', + 'int' + ), + 'a: int = 1' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.KEYWORD_ONLY, + None, + None + ), + 'a' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.KEYWORD_ONLY, + '1', + None + ), + 'a = 1' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.KEYWORD_ONLY, + None, + 'int' + ), + 'a: int' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.KEYWORD_ONLY, + '1', + 'int' + ), + 'a: int = 1' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.VARARG, + None, + None + ), + '*a' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.VARARG, + None, + 'int' + ), + '*a: int' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.KWARGS, + None, + None + ), + '**a' + ), + ( + PythonFunctionArgument( + 'a', + PythonArgumentKind.KWARGS, + None, + 'int' + ), + '**a: int' + ), ]) def test_PythonFunctionArgument_signature_repr(argument, expected): assert argument.signature_repr() == expected diff --git a/tests/unit/core/python/definitions/test_PythonClassDefinition.py b/tests/unit/core/python/definitions/test_PythonClassDefinition.py index cad9ae9..2d94b84 100644 --- a/tests/unit/core/python/definitions/test_PythonClassDefinition.py +++ b/tests/unit/core/python/definitions/test_PythonClassDefinition.py @@ -12,86 +12,482 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.jcloud_docsgen.core.python.definitions import PythonFunctionDefinition, PythonClassDefinition -from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind +from src.jcloud_docsgen.core.python.definitions import ( + PythonFunctionDefinition, + PythonClassDefinition +) +from src.jcloud_docsgen.core.python.arguments import ( + PythonFunctionArgument, + PythonArgumentKind +) from src.jcloud_docsgen.core.python.docstrings import PythonDocstring from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError from tests.utils.ast_node import ast_node import pytest @pytest.mark.parametrize('definition,expected', [ - (PythonClassDefinition('A', [], None, None, []), 'PythonClassDefinition(\'A\', [], None, None, [])'), - (PythonClassDefinition('A', [], None, PythonDocstring('Hello, World!'), []), 'PythonClassDefinition(\'A\', [], None, PythonDocstring(\'Hello, World!\'), [])'), + ( + PythonClassDefinition( + 'A', + [], + None, + None, + [] + ), + 'PythonClassDefinition(\'A\', [], None, None, [])' + ), + ( + PythonClassDefinition( + 'A', + [], + None, + PythonDocstring('Hello, World!'), + [] + ), + ( + 'PythonClassDefinition(\'A\', [], None, ' + 'PythonDocstring(\'Hello, World!\'), [])' + ) + ), ]) def test_PythonDefinition_string_representation(definition, expected): assert repr(definition) == expected -@pytest.mark.parametrize('name,args,returns,doc,decorators,expected_exception,expected_exception_message', [ - ('', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier'), - ('.', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: .'), - ('#', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: #'), - ('1a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: 1a'), - ('1_', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: 1_'), - ('1,', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: 1,'), - (',', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ,'), - (':', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: :'), - ('-', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: -'), - (';', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ;'), - ('|', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: |'), - ('<', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: <'), - ('>', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: >'), - ('a,', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a,'), - (',a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ,a'), - ('a:', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a:'), - (':a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: :a'), - ('a-', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a-'), - ('-a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: -a'), - ('a;', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a;'), - (';a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ;a'), - ('a|', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a|'), - ('|a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: |a'), - ('a<', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a<'), - ('', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a>'), - ('>a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: >a'), - ('class', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: class'), - ('def', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: def'), - ('lambda', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: lambda'), - ('None', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: None'), - ('True', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: True'), -]) -def test_PythonFunctionDefinition_exceptions(name, args, returns, doc, decorators, expected_exception, expected_exception_message): +@pytest.mark.parametrize( + ( + 'name,args,returns,doc,decorators,expect' + 'ed_exception,expected_exception_message' + ), + [ + ( + '', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier' + ), + ( + '.', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: .' + ), + ( + '#', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: #' + ), + ( + '1a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1a' + ), + ( + '1_', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1_' + ), + ( + '1,', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1,' + ), + ( + ',', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ,' + ), + ( + ':', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: :' + ), + ( + '-', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: -' + ), + ( + ';', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ;' + ), + ( + '|', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: |' + ), + ( + '<', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: <' + ), + ( + '>', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: >' + ), + ( + 'a,', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a,' + ), + ( + ',a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ,a' + ), + ( + 'a:', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a:' + ), + ( + ':a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: :a' + ), + ( + 'a-', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a-' + ), + ( + '-a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: -a' + ), + ( + 'a;', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a;' + ), + ( + ';a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ;a' + ), + ( + 'a|', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a|' + ), + ( + '|a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: |a' + ), + ( + 'a<', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a<' + ), + ( + '', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a>' + ), + ( + '>a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: >a' + ), + ( + 'class', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: class' + ), + ( + 'def', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: def' + ), + ( + 'lambda', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: lambda' + ), + ( + 'None', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: None' + ), + ( + 'True', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: True' + ), + ] +) +def test_PythonFunctionDefinition_exceptions( + name, + args, + returns, + doc, + decorators, + expected_exception, + expected_exception_message +): with pytest.raises(expected_exception) as exc_info: PythonClassDefinition(name, args, returns, doc, decorators) assert str(exc_info.value) == expected_exception_message @pytest.mark.parametrize('node,expected', [ - (ast_node('@decorator\nclass Class(BaseClass1, BaseClass2):\n\tdef __init__(self, value: object) -> None:\n\t\tself._value = value\n\n\t@property\n\tdef value(self) -> object:\n\t\treturn self._value'), PythonClassDefinition('Class', [ - 'BaseClass1', - 'BaseClass2' - ], None, ['decorator'], [ - PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None), - PythonFunctionArgument('value', PythonArgumentKind.NORMAL, None, 'object') - ], 'None', None, []), - PythonFunctionDefinition('value', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None) - ], 'object', None, ['property']) - ])), - (ast_node('class Class: ...'), PythonClassDefinition('Class', [], None, [], [])), - (ast_node('class Class: \'\'\'docstring\'\'\''), PythonClassDefinition('Class', [], PythonDocstring('docstring'), [], [])), - (ast_node('class Class:\n\tdef __init__(self): ...'), PythonClassDefinition('Class', [], None, [], [ - PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None) - ], None, None, []) - ])), - (ast_node('class Class:\n\t\'\'\'doc\'\'\'\n\tdef __init__(self): ...'), PythonClassDefinition('Class', [], PythonDocstring('doc'), [], [ - PythonFunctionDefinition('__init__', [ - PythonFunctionArgument('self', PythonArgumentKind.NORMAL, None, None) - ], None, None, []) - ])), + ( + ast_node( + ( + '@decorator\nclass Class(BaseClass1, BaseClass2)' + ':\n\tdef __init__(self, value: object) -> None:' + '\n\t\tself._value = value\n\n\t@property\n\tdef' + ' value(self) -> object:\n\t\treturn self._value' + ) + + ), + PythonClassDefinition( + 'Class', + ['BaseClass1', 'BaseClass2'], + None, + ['decorator'], + [ + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ), + PythonFunctionArgument( + 'value', + PythonArgumentKind.NORMAL, + None, + 'object' + ) + ], + 'None', + None, + [] + ), + PythonFunctionDefinition( + 'value', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + 'object', + None, + ['property'] + ) + ] + ) + ), + ( + ast_node('class Class: ...'), + PythonClassDefinition( + 'Class', + [], + None, + [], + [] + ) + ), + ( + ast_node('class Class: \'\'\'docstring\'\'\''), + PythonClassDefinition( + 'Class', + [], + PythonDocstring('docstring'), + [], + [] + ) + ), + ( + ast_node('class Class:\n\tdef __init__(self): ...'), + PythonClassDefinition( + 'Class', + [], + None, + [], + [ + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + None, + None, + [] + ) + ] + ) + ), + ( + ast_node('class Class:\n\t\'\'\'doc\'\'\'\n\tdef __init__(self): ...'), + PythonClassDefinition( + 'Class', + [], + PythonDocstring('doc'), + [], + [ + PythonFunctionDefinition( + '__init__', + [ + PythonFunctionArgument( + 'self', + PythonArgumentKind.NORMAL, + None, + None + ) + ], + None, + None, + [] + ) + ] + ) + ), ]) def test_PythonDefinition_from_node(node, expected): assert PythonClassDefinition.from_node(node) == expected diff --git a/tests/unit/core/python/definitions/test_PythonDefinition.py b/tests/unit/core/python/definitions/test_PythonDefinition.py index 1e3a4a1..cb05aec 100644 --- a/tests/unit/core/python/definitions/test_PythonDefinition.py +++ b/tests/unit/core/python/definitions/test_PythonDefinition.py @@ -18,66 +18,425 @@ from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError import pytest @pytest.mark.parametrize('definition,expected', [ - (PythonDefinition('a', None, []), 'PythonDefinition(\'a\', None, [])'), - (PythonDefinition('a', PythonDocstring('Hello, World!'), []), 'PythonDefinition(\'a\', PythonDocstring(\'Hello, World!\'), [])'), + ( + PythonDefinition( + 'a', + None, + [] + ), + 'PythonDefinition(\'a\', None, [])' + ), + ( + PythonDefinition( + 'a', + PythonDocstring('Hello, World!'), + [] + ), + 'PythonDefinition(\'a\', PythonDocstring(\'Hello, World!\'), [])' + ), ]) def test_PythonDefinition_string_representation(definition, expected): assert repr(definition) == expected -@pytest.mark.parametrize('name,doc,decorators,expected_exception,expected_exception_message', [ - ('', None, [], InvalidPythonIdentifierError, 'invalid identifier'), - ('.', None, [], InvalidPythonIdentifierError, 'invalid identifier: .'), - ('#', None, [], InvalidPythonIdentifierError, 'invalid identifier: #'), - ('1a', None, [], InvalidPythonIdentifierError, 'invalid identifier: 1a'), - ('1_', None, [], InvalidPythonIdentifierError, 'invalid identifier: 1_'), - ('1,', None, [], InvalidPythonIdentifierError, 'invalid identifier: 1,'), - (',', None, [], InvalidPythonIdentifierError, 'invalid identifier: ,'), - (':', None, [], InvalidPythonIdentifierError, 'invalid identifier: :'), - ('-', None, [], InvalidPythonIdentifierError, 'invalid identifier: -'), - (';', None, [], InvalidPythonIdentifierError, 'invalid identifier: ;'), - ('|', None, [], InvalidPythonIdentifierError, 'invalid identifier: |'), - ('<', None, [], InvalidPythonIdentifierError, 'invalid identifier: <'), - ('>', None, [], InvalidPythonIdentifierError, 'invalid identifier: >'), - ('a,', None, [], InvalidPythonIdentifierError, 'invalid identifier: a,'), - (',a', None, [], InvalidPythonIdentifierError, 'invalid identifier: ,a'), - ('a:', None, [], InvalidPythonIdentifierError, 'invalid identifier: a:'), - (':a', None, [], InvalidPythonIdentifierError, 'invalid identifier: :a'), - ('a-', None, [], InvalidPythonIdentifierError, 'invalid identifier: a-'), - ('-a', None, [], InvalidPythonIdentifierError, 'invalid identifier: -a'), - ('a;', None, [], InvalidPythonIdentifierError, 'invalid identifier: a;'), - (';a', None, [], InvalidPythonIdentifierError, 'invalid identifier: ;a'), - ('a|', None, [], InvalidPythonIdentifierError, 'invalid identifier: a|'), - ('|a', None, [], InvalidPythonIdentifierError, 'invalid identifier: |a'), - ('a<', None, [], InvalidPythonIdentifierError, 'invalid identifier: a<'), - ('', None, [], InvalidPythonIdentifierError, 'invalid identifier: a>'), - ('>a', None, [], InvalidPythonIdentifierError, 'invalid identifier: >a'), - ('class', None, [], InvalidPythonIdentifierError, 'invalid identifier: class'), - ('def', None, [], InvalidPythonIdentifierError, 'invalid identifier: def'), - ('lambda', None, [], InvalidPythonIdentifierError, 'invalid identifier: lambda'), - ('None', None, [], InvalidPythonIdentifierError, 'invalid identifier: None'), - ('True', None, [], InvalidPythonIdentifierError, 'invalid identifier: True'), -]) -def test_PythonDefinition_exceptions(name, doc, decorators, expected_exception, expected_exception_message): +@pytest.mark.parametrize( + 'name,doc,decorators,expected_exception,expected_exception_message', + [ + ( + '', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier' + ), + ( + '.', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: .' + ), + ( + '#', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: #' + ), + ( + '1a', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1a' + ), + ( + '1_', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1_' + ), + ( + '1,', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1,' + ), + ( + ',', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ,' + ), + ( + ':', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: :' + ), + ( + '-', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: -' + ), + ( + ';', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ;' + ), + ( + '|', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: |' + ), + ( + '<', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: <' + ), + ( + '>', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: >' + ), + ( + 'a,', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a,' + ), + ( + ',a', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ,a' + ), + ( + 'a:', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a:' + ), + ( + ':a', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: :a' + ), + ( + 'a-', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a-' + ), + ( + '-a', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: -a' + ), + ( + 'a;', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a;' + ), + ( + ';a', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ;a' + ), + ( + 'a|', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a|' + ), + ( + '|a', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: |a' + ), + ( + 'a<', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a<' + ), + ( + '', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a>' + ), + ( + '>a', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: >a' + ), + ( + 'class', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: class' + ), + ( + 'def', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: def' + ), + ( + 'lambda', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: lambda' + ), + ( + 'None', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: None' + ), + ( + 'True', + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: True' + ), + ] +) +def test_PythonDefinition_exceptions( + name, + doc, + decorators, + expected_exception, + expected_exception_message +): with pytest.raises(expected_exception) as exc_info: PythonDefinition(name, doc, decorators) assert str(exc_info.value) == expected_exception_message @pytest.mark.parametrize('definition1,definition2,expected', [ - (PythonDefinition('a', None, []), PythonDefinition('a', None, []), True), - (PythonDefinition('a', PythonDocstring('doc'), []), PythonDefinition('a', PythonDocstring('doc'), []), True), - (PythonDefinition('a', PythonDocstring('doc'), ['a']), PythonDefinition('a', PythonDocstring('doc'), ['a']), True), - (PythonDefinition('a', PythonDocstring('doc'), ['a', 'b']), PythonDefinition('a', PythonDocstring('doc'), ['a', 'b']), True), - (PythonDefinition('a', None, []), PythonDefinition('b', None, []), False), - (PythonDefinition('a', PythonDocstring('doc'), []), PythonDefinition('a', None, []), False), - (PythonDefinition('a', PythonDocstring('doc'), []), PythonDefinition('a', PythonDocstring('docs'), []), False), - (PythonDefinition('a', None, ['a']), PythonDefinition('a', None, ['b']), False), - (PythonDefinition('a', None, ['a']), PythonDefinition('a', None, []), False), - (PythonDefinition('a', None, ['a']), PythonDefinition('a', None, ['a', 'b']), False), - (PythonDefinition('a', None, ['b', 'a']), PythonDefinition('a', None, ['a', 'b']), False), - (PythonDefinition('a', PythonDocstring('doc'), []), PythonDefinition('b', None, ['a', 'b']), False), + ( + PythonDefinition( + 'a', + None, + [] + ), + PythonDefinition( + 'a', + None, + [] + ), + True + ), + ( + PythonDefinition( + 'a', + PythonDocstring('doc'), + [] + ), + PythonDefinition( + 'a', + PythonDocstring('doc'), + [] + ), + True + ), + ( + PythonDefinition( + 'a', + PythonDocstring('doc'), + ['a'] + ), + PythonDefinition( + 'a', + PythonDocstring('doc'), + ['a'] + ), + True + ), + ( + PythonDefinition( + 'a', + PythonDocstring('doc'), + ['a', 'b'] + ), + PythonDefinition( + 'a', + PythonDocstring('doc'), + ['a', 'b'] + ), + True + ), + ( + PythonDefinition( + 'a', + None, + [] + ), + PythonDefinition( + 'b', + None, + [] + ), + False + ), + ( + PythonDefinition( + 'a', + PythonDocstring('doc'), + [] + ), + PythonDefinition( + 'a', + None, + [] + ), + False + ), + ( + PythonDefinition( + 'a', + PythonDocstring('doc'), + [] + ), + PythonDefinition( + 'a', + PythonDocstring('docs'), + [] + ), + False + ), + ( + PythonDefinition( + 'a', + None, + ['a'] + ), + PythonDefinition( + 'a', + None, + ['b'] + ), + False + ), + ( + PythonDefinition( + 'a', + None, + ['a'] + ), + PythonDefinition( + 'a', + None, + [] + ), + False + ), + ( + PythonDefinition( + 'a', + None, + ['a'] + ), + PythonDefinition( + 'a', + None, + ['a', 'b'] + ), + False + ), + ( + PythonDefinition( + 'a', + None, + ['b', 'a'] + ), + PythonDefinition( + 'a', + None, + ['a', 'b'] + ), + False + ), + ( + PythonDefinition( + 'a', + PythonDocstring('doc'), + [] + ), + PythonDefinition( + 'b', + None, + ['a', 'b'] + ), + False + ), ]) def test_PythonDefinition___eq__(definition1, definition2, expected): assert (definition1 == definition2) == expected diff --git a/tests/unit/core/python/definitions/test_PythonFunctionDefinition.py b/tests/unit/core/python/definitions/test_PythonFunctionDefinition.py index a97b3e2..09b67a5 100644 --- a/tests/unit/core/python/definitions/test_PythonFunctionDefinition.py +++ b/tests/unit/core/python/definitions/test_PythonFunctionDefinition.py @@ -13,75 +13,447 @@ # limitations under the License. from src.jcloud_docsgen.core.python.definitions import PythonFunctionDefinition -from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind +from src.jcloud_docsgen.core.python.arguments import ( + PythonFunctionArgument, + PythonArgumentKind +) from src.jcloud_docsgen.core.python.docstrings import PythonDocstring from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError from tests.utils.ast_node import ast_node import pytest @pytest.mark.parametrize('definition,expected', [ - (PythonFunctionDefinition('a', [], None, None, []), 'PythonFunctionDefinition(\'a\', [], None, None, [])'), - (PythonFunctionDefinition('a', [], None, PythonDocstring('Hello, World!'), []), 'PythonFunctionDefinition(\'a\', [], None, PythonDocstring(\'Hello, World!\'), [])'), + ( + PythonFunctionDefinition( + 'a', + [], + None, + None, + [] + ), + 'PythonFunctionDefinition(\'a\', [], None, None, [])' + ), + ( + PythonFunctionDefinition( + 'a', + [], + None, + PythonDocstring('Hello, World!'), + [] + ), + ( + 'PythonFunctionDefinition(\'a\', [], None,' + ' PythonDocstring(\'Hello, World!\'), [])' + ) + ), ]) def test_PythonDefinition_string_representation(definition, expected): assert repr(definition) == expected -@pytest.mark.parametrize('name,args,returns,doc,decorators,expected_exception,expected_exception_message', [ - ('', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier'), - ('.', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: .'), - ('#', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: #'), - ('1a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: 1a'), - ('1_', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: 1_'), - ('1,', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: 1,'), - (',', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ,'), - (':', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: :'), - ('-', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: -'), - (';', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ;'), - ('|', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: |'), - ('<', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: <'), - ('>', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: >'), - ('a,', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a,'), - (',a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ,a'), - ('a:', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a:'), - (':a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: :a'), - ('a-', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a-'), - ('-a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: -a'), - ('a;', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a;'), - (';a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: ;a'), - ('a|', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a|'), - ('|a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: |a'), - ('a<', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a<'), - ('', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: a>'), - ('>a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: >a'), - ('class', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: class'), - ('def', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: def'), - ('lambda', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: lambda'), - ('None', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: None'), - ('True', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: True'), +@pytest.mark.parametrize( + ( + 'name,args,returns,doc,decorators,expect' + 'ed_exception,expected_exception_message' + ), + [ + ( + '', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier' + ), + ( + '.', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: .' + ), + ( + '#', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: #' + ), + ( + '1a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1a' + ), + ( + '1_', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1_' + ), + ( + '1,', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: 1,' + ), + ( + ',', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ,' + ), + ( + ':', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: :' + ), + ( + '-', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: -' + ), + ( + ';', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ;' + ), + ( + '|', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: |' + ), + ( + '<', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: <' + ), + ( + '>', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: >' + ), + ( + 'a,', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a,' + ), + ( + ',a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ,a' + ), + ( + 'a:', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a:' + ), + ( + ':a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: :a' + ), + ( + 'a-', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a-' + ), + ( + '-a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: -a' + ), + ( + 'a;', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a;' + ), + ( + ';a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: ;a' + ), + ( + 'a|', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a|' + ), + ( + '|a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: |a' + ), + ( + 'a<', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a<' + ), + ( + '', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: a>' + ), + ( + '>a', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: >a' + ), + ( + 'class', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: class' + ), + ( + 'def', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: def' + ), + ( + 'lambda', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: lambda' + ), + ( + 'None', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: None' + ), + ( + 'True', + [], + None, + None, + [], + InvalidPythonIdentifierError, + 'invalid identifier: True' + ), ]) -def test_PythonFunctionDefinition_exceptions(name, args, returns, doc, decorators, expected_exception, expected_exception_message): +def test_PythonFunctionDefinition_exceptions( + name, + args, + returns, + doc, + decorators, + expected_exception, + expected_exception_message +): with pytest.raises(expected_exception) as exc_info: PythonFunctionDefinition(name, args, returns, doc, decorators) assert str(exc_info.value) == expected_exception_message @pytest.mark.parametrize('node,expected', [ - (ast_node('def add(a: int, b: int) -> int: return a + b'), PythonFunctionDefinition('add', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int'), - ], 'int', None, [])), - (ast_node('@decorator\ndef add(a: int, b: int) -> int: return a + b'), PythonFunctionDefinition('add', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int'), - ], 'int', None, ['decorator'])), - (ast_node('@decorator1\n@decorator2\ndef add(a: int, b: int) -> int: return a + b'), PythonFunctionDefinition('add', [ - PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'), - PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int'), - ], 'int', None, ['decorator1', 'decorator2'])), - (ast_node('def func(): ...'), PythonFunctionDefinition('func', [], None, None, [])), - (ast_node('@decorator\ndef func(): ...'), PythonFunctionDefinition('func', [], None, None, ['decorator'])), + ( + ast_node('def add(a: int, b: int) -> int: return a + b'), + PythonFunctionDefinition( + 'add', [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + ], + 'int', + None, + [] + ) + ), + ( + ast_node('@decorator\ndef add(a: int, b: int) -> int: return a + b'), + PythonFunctionDefinition( + 'add', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + ], + 'int', + None, + ['decorator'] + ) + ), + ( + ast_node( + ( + '@decorator1\n@decorator2\ndef add(a' + ': int, b: int) -> int: return a + b' + ) + ), + PythonFunctionDefinition( + 'add', + [ + PythonFunctionArgument( + 'a', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + PythonFunctionArgument( + 'b', + PythonArgumentKind.NORMAL, + None, + 'int' + ), + ], + 'int', + None, + ['decorator1', 'decorator2'] + ) + ), + ( + ast_node('def func(): ...'), + PythonFunctionDefinition( + 'func', + [], + None, + None, + [] + ) + ), + ( + ast_node('@decorator\ndef func(): ...'), + PythonFunctionDefinition( + 'func', + [], + None, + None, + ['decorator'] + ) + ), ]) def test_PythonDefinition_from_node(node, expected): assert PythonFunctionDefinition.from_node(node) == expected diff --git a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py index 3cc80e8..c8f873b 100644 --- a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py @@ -12,16 +12,40 @@ # See the License for the specific language governing permissions and # limitations under the License. -from src.jcloud_docsgen.core.python.namespaces import PythonPackageNamespace, PythonModuleNamespace +from src.jcloud_docsgen.core.python.namespaces import ( + PythonPackageNamespace, + PythonModuleNamespace +) import pytest -from src.jcloud_docsgen.exceptions import NamespaceNotFoundError, InvalidNamespaceError +from src.jcloud_docsgen.exceptions import ( + NamespaceNotFoundError, + InvalidNamespaceError +) class StrSubclass(str): ... @pytest.mark.parametrize('namespace,expected_name', [ - (PythonPackageNamespace('a', []), 'a'), - (PythonPackageNamespace('1', []), '1'), - (PythonPackageNamespace(StrSubclass('a'), []), StrSubclass('a')), + ( + PythonPackageNamespace( + 'a', + [] + ), + 'a' + ), + ( + PythonPackageNamespace( + '1', + [] + ), + '1' + ), + ( + PythonPackageNamespace( + StrSubclass('a'), + [] + ), + StrSubclass('a') + ), ]) def test_PythonPackageNamespace_name_attribute(namespace, expected_name): assert namespace.name == expected_name @@ -41,68 +65,225 @@ namespace_e = PythonPackageNamespace('e', [namespace_f]) namespace_d = PythonPackageNamespace('d', [namespace_e]) @pytest.mark.parametrize('namespace,sub_namespaces,expected', [ - (PythonPackageNamespace('name', [ - namespace_a, + ( + PythonPackageNamespace( + 'name', + [namespace_a, namespace_c] + ), + ['c'], namespace_c - ]), ['c'], namespace_c), - (PythonPackageNamespace('name', [ - namespace_a, - namespace_c - ]), ['a'], namespace_a), - (PythonPackageNamespace('name', [ - namespace_a, - namespace_c - ]), ['a', 'b'], namespace_b), - (namespace_d, ['e', 'f', 'g', 'h', 'i'], namespace_i), - (namespace_d, ['e', 'f', 'g', 'h', 'i', 'j'], namespace_j), - (namespace_d, ['e', 'f', 'g', 'h', 'i', 'j', 'k'], namespace_k), + ), + ( + PythonPackageNamespace( + 'name', + [namespace_a, namespace_c] + ), + ['a'], + namespace_a + ), + ( + PythonPackageNamespace( + 'name', + [namespace_a, namespace_c] + ), + ['a', 'b'], + namespace_b + ), + ( + namespace_d, + ['e', 'f', 'g', 'h', 'i'], + namespace_i + ), + ( + namespace_d, + ['e', 'f', 'g', 'h', 'i', 'j'], + namespace_j + ), + ( + namespace_d, + ['e', 'f', 'g', 'h', 'i', 'j', 'k'], + namespace_k + ), ]) -def test_PythonPackageNamespace_namespace(namespace: PythonPackageNamespace, sub_namespaces, expected): +def test_PythonPackageNamespace_namespace( + namespace: PythonPackageNamespace, + sub_namespaces, + expected +): assert namespace.namespace(sub_namespaces) == expected -@pytest.mark.parametrize('namespace,sub_namespaces,expected_exception,expected_exception_msg', [ - (namespace_a, ['c'], NamespaceNotFoundError, 'no such namespace: c'), - (namespace_h, ['i', 'k'], NamespaceNotFoundError, 'no such namespace: k'), - (namespace_a, ['b', ''], InvalidNamespaceError, 'invalid namespace'), - (namespace_a, ['', 'b'], InvalidNamespaceError, 'invalid namespace'), - (namespace_a, ['b', '', 'b'], InvalidNamespaceError, 'invalid namespace'), -]) -def test_PythonPackageNamespace_namespace_exceptions(namespace, sub_namespaces, expected_exception, expected_exception_msg): +@pytest.mark.parametrize( + 'namespace,sub_namespaces,expected_exception,expected_exception_msg', + [ + ( + namespace_a, + ['c'], + NamespaceNotFoundError, + 'no such namespace: c' + ), + ( + namespace_h, + ['i', 'k'], + NamespaceNotFoundError, + 'no such namespace: k' + ), + ( + namespace_a, + ['b', ''], + InvalidNamespaceError, + 'invalid namespace' + ), + ( + namespace_a, + ['', 'b'], + InvalidNamespaceError, + 'invalid namespace' + ), + ( + namespace_a, + ['b', '', 'b'], + InvalidNamespaceError, + 'invalid namespace' + ), + ] +) +def test_PythonPackageNamespace_namespace_exceptions( + namespace, + sub_namespaces, + expected_exception, + expected_exception_msg +): with pytest.raises(expected_exception) as exc_info: namespace.namespace(sub_namespaces) assert str(exc_info.value) == expected_exception_msg @pytest.mark.parametrize('namespace,expected', [ - (PythonPackageNamespace('a', [PythonModuleNamespace('b')]), {'b': 'b'}), - (PythonPackageNamespace('a', [PythonModuleNamespace('b'), PythonPackageNamespace('c', [PythonModuleNamespace('d')])]), {'b': 'b', 'c': {'d': 'd'}}), - (PythonPackageNamespace('a', []), {}) + ( + PythonPackageNamespace( + 'a', + [PythonModuleNamespace('b')] + ), + {'b': 'b'} + ), + ( + PythonPackageNamespace( + 'a', + [ + PythonModuleNamespace('b'), + PythonPackageNamespace( + 'c', + [PythonModuleNamespace('d')] + ) + ] + ), + { + 'b': 'b', + 'c': {'d': 'd'} + } + ), + ( + PythonPackageNamespace('a', []), + {} + ) ]) def test_PythonPackageNamespace_sub_namespace_names(namespace, expected): assert namespace.sub_namespace_names() == expected @pytest.mark.parametrize('namespace1,namespace2,expected', [ - (PythonPackageNamespace('a', [PythonModuleNamespace('b')]), PythonPackageNamespace('a', [PythonModuleNamespace('b')]), True), - (PythonPackageNamespace('a', [PythonModuleNamespace('b'), PythonPackageNamespace('c', [PythonModuleNamespace('d')])]), PythonPackageNamespace('a', [PythonModuleNamespace('b'), PythonPackageNamespace('c', [PythonModuleNamespace('d')])]), True), - (PythonPackageNamespace('a', []), PythonPackageNamespace('a', []), True), - (PythonPackageNamespace('a', []), PythonPackageNamespace('b', []), False), + ( + PythonPackageNamespace( + 'a', + [PythonModuleNamespace('b')] + ), + PythonPackageNamespace( + 'a', + [PythonModuleNamespace('b')] + ), + True + ), + ( + PythonPackageNamespace( + 'a', + [ + PythonModuleNamespace('b'), + PythonPackageNamespace( + 'c', + [PythonModuleNamespace('d')] + ) + ] + ), + PythonPackageNamespace( + 'a', + [ + PythonModuleNamespace('b'), + PythonPackageNamespace( + 'c', + [PythonModuleNamespace('d')] + ) + ] + ), + True + ), + ( + PythonPackageNamespace('a', []), + PythonPackageNamespace('a', []), + True + ), + ( + PythonPackageNamespace('a', []), + PythonPackageNamespace('b', []), + False + ), ]) def test_PythonPackageNamespace___eq__(namespace1, namespace2, expected): assert (namespace1 == namespace2) == expected assert (namespace2 == namespace1) == expected @pytest.mark.parametrize('namespace,expected', [ - (PythonPackageNamespace('a', []), 'PythonPackageNamespace(\'a\', [])'), - (PythonPackageNamespace('1', []), 'PythonPackageNamespace(\'1\', [])'), - (PythonPackageNamespace(StrSubclass('a'), []), 'PythonPackageNamespace(\'a\', [])'), - (namespace_a, 'PythonPackageNamespace(\'a\', [PythonModuleNamespace(\'b\')])'), - (namespace_d, 'PythonPackageNamespace(\'d\', [PythonPackageNamespace(\'e\', [PythonPackageNamespace(\'f\', [PythonPackageNamespace(\'g\', [PythonPackageNamespace(\'h\', [PythonPackageNamespace(\'i\', [PythonPackageNamespace(\'j\', [PythonModuleNamespace(\'k\')])])])])])])])') + ( + PythonPackageNamespace('a', []), + 'PythonPackageNamespace(\'a\', [])' + ), + ( + PythonPackageNamespace('1', []), + 'PythonPackageNamespace(\'1\', [])' + ), + ( + PythonPackageNamespace(StrSubclass('a'), []), + 'PythonPackageNamespace(\'a\', [])' + ), + ( + namespace_a, + 'PythonPackageNamespace(\'a\', [PythonModuleNamespace(\'b\')])' + ), + ( + namespace_d, + ( + 'PythonPackageNamespace(\'d\', [PythonPackageNamespac' + 'e(\'e\', [PythonPackageNamespace(\'f\', [PythonPacka' + 'geNamespace(\'g\', [PythonPackageNamespace(\'h\', [P' + 'ythonPackageNamespace(\'i\', [PythonPackageNamespace' + '(\'j\', [PythonModuleNamespace(\'k\')])])])])])])])' + ) + ) ]) def test_PythonPackageNamespace_string_representation(namespace, expected): assert repr(namespace) == expected @pytest.mark.parametrize('namespace,new_namespace,expected', [ - (PythonPackageNamespace('a', []), PythonModuleNamespace('b'), PythonPackageNamespace('a', [PythonModuleNamespace('b')])) + ( + PythonPackageNamespace('a', []), + PythonModuleNamespace('b'), + PythonPackageNamespace( + 'a', + [PythonModuleNamespace('b')] + ) + ) ]) -def test_PythonPackageNamespace___getitem__(namespace, new_namespace, expected): +def test_PythonPackageNamespace___getitem__( + namespace, + new_namespace, + expected +): namespace.add(new_namespace) assert namespace == expected diff --git a/tests/unit/utils/test_assert_that_is_instance.py b/tests/unit/utils/test_assert_that_is_instance.py index c479ba1..d95fe92 100644 --- a/tests/unit/utils/test_assert_that_is_instance.py +++ b/tests/unit/utils/test_assert_that_is_instance.py @@ -31,13 +31,37 @@ def test_assert_that_is_instance(obj, class_or_tuple): assert_that_is_instance(obj, class_or_tuple) @pytest.mark.parametrize('obj,class_or_tuple,expected_exception_msg', [ - (1, str, 'expected \'str\', got \'int\''), - (1, (str,), 'expected \'str\', got \'int\''), - (1, str | float, 'expected either \'str\' or \'float\', got \'int\''), - (1, str | float | list, 'expected either \'str\', \'float\' or \'list\', got \'int\''), - (1, TestType, 'expected \'NOT TestType\', got \'int\''), + ( + 1, + str, + 'expected \'str\', got \'int\'' + ), + ( + 1, + (str,), + 'expected \'str\', got \'int\'' + ), + ( + 1, + str | float, + 'expected either \'str\' or \'float\', got \'int\'' + ), + ( + 1, + str | float | list, + 'expected either \'str\', \'float\' or \'list\', got \'int\'' + ), + ( + 1, + TestType, + 'expected \'NOT TestType\', got \'int\'' + ), ]) -def test_assert_that_is_instance_exceptions(obj, class_or_tuple, expected_exception_msg): +def test_assert_that_is_instance_exceptions( + obj, + class_or_tuple, + expected_exception_msg +): with pytest.raises(TypeError) as exc_info: assert_that_is_instance(obj, class_or_tuple) assert str(exc_info.value) == expected_exception_msg diff --git a/tests/unit/utils/test_human_readable_list.py b/tests/unit/utils/test_human_readable_list.py index 7d68be2..f97854d 100644 --- a/tests/unit/utils/test_human_readable_list.py +++ b/tests/unit/utils/test_human_readable_list.py @@ -48,7 +48,12 @@ import pytest ([1, 2, 3], 'and', '\'', '\'1\', \'2\' and \'3\''), ([1, 2, 3], '', '\'', '\'1\', \'2\' \'3\''), ([1, 2, 3, 42, 'a', 23], 'and', '', '1, 2, 3, 42, a and 23'), - ([1, 2, 3, 42, 'a', 23], 'and', '\'', '\'1\', \'2\', \'3\', \'42\', \'a\' and \'23\''), + ( + [1, 2, 3, 42, 'a', 23], + 'and', + '\'', + '\'1\', \'2\', \'3\', \'42\', \'a\' and \'23\'' + ), ]) def test_human_readable_list(ls, final_separator, quotation_mark, expected): assert human_readable_list(ls, final_separator, quotation_mark) == expected