Merge pull request 'Style: ensure line character limit and remove trailing whitespace' (#19) from style/pip-8-compliance into main

Reviewed-on: #19
This commit was merged in pull request #19.
This commit is contained in:
jakob.scheid authored and Gitea committed 2026-09-18 17:12:59 +02:00
commit ffcc7404de
26 files changed
+6754 -1099

No files matched your search

+20 -5
View File
@@ -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
+14 -4
View File
@@ -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:
+213 -56
View File
@@ -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,13 +137,19 @@ 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 = []
for arg in positional_only_args:
args.append(arg.signature_repr())
if positional_only_args:
args.append('/')
@@ -130,7 +164,7 @@ def _signature_argument_list(argument_list: list[PythonFunctionArgument]) -> lis
for arg in keyword_only_args:
args.append(arg.signature_repr())
if kwargs is not None:
args.append(kwargs.signature_repr())
@@ -157,10 +191,18 @@ 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.
If ``allow_markdown_table`` is ``False``, an alternative
representation is used.
@@ -184,22 +226,38 @@ def _markdown_table(header: tuple, body: list[tuple], *, allow_markdown_table: b
if not header or not body:
return ''
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 += '<details><summary><h' + str(self.level + 1) + ' style="display:inline">Docstring' + '</h' + str(self.level + 1) + '></summary>'
md += f'<details><summary><h{str(
self.level + 1
)} style="display:inline">Docstring</h{str(
self.level + 1
)}></summary>'
else:
md += self.level * '#' + '# Docstring\n\n'
@@ -485,7 +593,7 @@ class PythonDefinitionDocumentationGenerator:
md += self.definition.doc.docstring
elif not self.skip_empty_sections:
md += 'There is no docstring to show.'
if _is_docstring(self.definition.doc) or not self.skip_empty_sections:
if self.allow_html:
md += '</details>'
@@ -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'
@@ -722,10 +859,10 @@ class PythonDefinitionDocumentationGenerator:
if isinstance(self.definition, PythonClassDefinition):
if self.include_sections.bases:
md += self._generate_bases_section() + '\n\n'
if self.include_sections.class_body:
md += self._generate_body_section() + '\n\n'
if self.include_sections.decorators:
md += self._generate_decorators_section() + '\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))
+139 -27
View File
@@ -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,27 +66,44 @@ 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((
self.name,
@@ -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
+32 -7
View File
@@ -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))
@@ -93,7 +101,7 @@ class PythonDefinition:
_python_docstring(ast.get_docstring(node)),
[ast.unparse(d) for d in node.decorator_list]
)
def __eq__(self, value: object) -> bool:
if not isinstance(value, PythonDefinition):
return False
@@ -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
@@ -202,7 +224,7 @@ class PythonClassDefinition(PythonDefinition):
self.decorators,
self.body
)
@classmethod
def from_node(cls, node: ast.stmt) -> PythonClassDefinition:
'''
@@ -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)
)
]
)
+3 -3
View File
@@ -22,7 +22,7 @@ __all__ = [
class PythonDocstring:
'''
Represents a Python docstring.
:param docstring: The docstring as a string.
:type docstring: str
'''
@@ -32,12 +32,12 @@ class PythonDocstring:
def __repr__(self) -> str:
return type(self).__name__ + '(' + repr(self.docstring) + ')'
def __eq__(self, value) -> bool:
if not isinstance(value, type(self)):
return False
return self.docstring == value.docstring
def parse(self) -> docstring_parser.Docstring:
'''
Parses the docstring.
+42 -12
View File
@@ -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,11 +111,14 @@ 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.
:param package: The package.
:type package: PythonPackageNamespace
@@ -107,15 +127,19 @@ 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
}
def sub_namespace_names(self) -> PythonPackageNamespaceDictType:
'''
Returns the names of all sub-namespaces and keeps the package
structure.
:return: The names if all sub-namespaces.
:rtype: PythonPackageNamespaceDictType
'''
@@ -151,8 +175,11 @@ 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:
'''
Adds a namespace.
@@ -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)
+37 -5
View File
@@ -24,8 +24,16 @@ 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): ...
class NamespaceExistsError(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): ...
+48 -15
View File
@@ -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)
@@ -62,7 +70,7 @@ class ExistingFile(pathlib.Path):
def _quote(string: str, quotation_mark: str) -> str:
'''
Quotes a string.
:param string: The string to quote
:type string: str
:param quotation_mark: The quotation mark
@@ -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.
@@ -86,7 +98,7 @@ def human_readable_list(ls: list, final_separator: str = 'and', quotation_mark:
:type final_separator: str
:param quotation_mark: The characters that are inserted before and
after an element. Default is ''.
:return: The human-readable list.
:rtype: str
'''
@@ -94,14 +106,17 @@ def human_readable_list(ls: list, final_separator: str = 'and', quotation_mark:
# empty list
if not ls:
return ''
# list with one element
if len(ls) == 1:
return _quote(str(ls[0]), 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,11 +156,16 @@ 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]:
'''
Returns the value or ``None``.
@@ -154,13 +184,16 @@ def non_empty_str(value: Union[str, None]) -> Union[str, None]:
if value is None:
return None
if value.strip() == '':
return 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``.
+3 -1
View File
@@ -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', [
(
File diff suppressed because it is too large. Load diff
@@ -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
@@ -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.
"""
@@ -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`
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds two numbers.
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds '''
'''two numbers.
:param a: The first number.
:type a: int | float
@@ -478,7 +700,8 @@ Return type: `str`
<details><summary><h5 style="display:inline">Docstring</h5></summary>Returns a greeting.
<details><summary><h5 style="display:inline">Docstring</h5></summary>'''
'''Returns a greeting.
:return: The greeting.
:rtype: str</details>
@@ -487,7 +710,8 @@ Return type: `str`
<details><summary><h3 style="display:inline">Docstring</h3></summary>A class representing an animal.
<details><summary><h3 style="display:inline">Docstring</h3></summary>A '''
'''class representing an animal.
:param name: Its name.
:type name: str</details>
@@ -528,15 +752,32 @@ Return type: `None`
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.</details>
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.'''
'''</details>
<details><summary><h3 style="display:inline">Docstring</h3></summary>A dog.</details>'''),
<details><summary><h3 style="display:inline">Docstring</h3></summary>A dog.'''
'</details>'
)
),
(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`
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds two numbers.
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds '''
'''two numbers.
:param a: The first number.
:type a: int | float
@@ -673,7 +915,8 @@ Return type: `str`
<details><summary><h5 style="display:inline">Docstring</h5></summary>Returns a greeting.
<details><summary><h5 style="display:inline">Docstring</h5></summary>'''
'''Returns a greeting.
:return: The greeting.
:rtype: str</details>
@@ -682,7 +925,8 @@ Return type: `str`
<details><summary><h3 style="display:inline">Docstring</h3></summary>A class representing an animal.
<details><summary><h3 style="display:inline">Docstring</h3></summary>A '''
'''class representing an animal.
:param name: Its name.
:type name: str</details>
@@ -729,15 +973,31 @@ Return type: `None`
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.</details>
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.'''
'''</details>
<details><summary><h3 style="display:inline">Docstring</h3></summary>A dog.</details>'''),
<details><summary><h3 style="display:inline">Docstring</h3></summary>A '''
'dog.</details>'
)
),
(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
@@ -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
@@ -49,39 +75,107 @@ class Something:
"""A class."""
def __init__(self, value: typing.Any = 42) -> None:
self._value = value
@property
def value(self) -> typing.Any:
"""
The value.
"""
return self.get_value()
def get_value(self) -> typing.Any:
"""
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
@@ -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
@@ -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
@@ -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):
File diff suppressed because it is too large. Load diff
@@ -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<'),
('<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'),
('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<'
),
(
'<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'
),
(
'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
@@ -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<'),
('<a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: <a'),
('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<'
),
(
'<a',
[],
None,
None,
[],
InvalidPythonIdentifierError,
'invalid identifier: <a'
),
(
'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
@@ -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<'),
('<a', None, [], InvalidPythonIdentifierError, 'invalid identifier: <a'),
('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<'
),
(
'<a',
None,
[],
InvalidPythonIdentifierError,
'invalid identifier: <a'
),
(
'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
@@ -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<'),
('<a', [], None, None, [], InvalidPythonIdentifierError, 'invalid identifier: <a'),
('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<'
),
(
'<a',
[],
None,
None,
[],
InvalidPythonIdentifierError,
'invalid identifier: <a'
),
(
'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
@@ -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
@@ -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
+6 -1
View File
@@ -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