generated from jCloud/repository-template
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:
26 files changed
+6754
-1099
No files matched your search
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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``.
|
||||
|
||||
|
||||
@@ -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
|
||||
+7
-1
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user