generated from jCloud/repository-template
Merge pull request 'Chore(lint): set up Ruff, lint and format' (#22) from chore/ruff into main
Reviewed-on: #22
This commit was merged in pull request #22.
This commit is contained in:
45 files changed
+1728
-1261
No files matched your search
Generated
+30
-2
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 2.4.3 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
@@ -143,6 +143,34 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pytest = ">=5.0"
|
pytest = ">=5.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruff"
|
||||||
|
version = "0.16.8"
|
||||||
|
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "ruff-0.16.8-py3-none-linux_armv6l.whl", hash = "sha256:6ffbd6d87383c1edf5f6fa890f10200950240d7c1a16052a19a09d3a2307dd38"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42ed6b878ed61e3acca92f2730a17acff39286944ea82398544696366a6f925e"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ea781c7f2afba8c6a505ea0fb3f994020249e0c450635f5381286fea6b46170"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8efeae3bbe414a5efefda11a792dfb51ef90ac48d50c4830de2f644caf3e8659"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a79b795469fef7fc6e908b218eed2eb17332afd85031db6480dc864560e69b2"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc5563cdc50555e6fba39322850860e9267c1b3d12c26a74729d8604c3c812"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34508983c70665578dab88f5223d8e6228307e1135398ca8bfc8b7e9501e282b"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644bb578569e0ffc575741232bd385dacdd6fbe123f1a729e7a225f54aa3957f"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e7d226246961db9235098333caa13063906d3851136b84c2900b82f5daa1df"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2bf6bc3e9ebdd4449abc6f06cf64b98051a2c61cf94d2fe9596518c881f1a1e"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ca111ba0849539165e9e59d2b442542f3c1e8060ebbdea82494f1ffbccb1e1f"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:359a1e5b495448ee1e91018064382ebc86f90e8aac2fed222c7d0e4e8df85fd2"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:59e8f5681349474110b24d62e93cfda6593f5fa3473446ca3705200cac1a08b9"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:efa3e7a16d1baaa79957888dfdf8be9ef2e44db81cb032af06d76632ab59e773"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-win32.whl", hash = "sha256:55793ba85c69921e89be061426d91a78652d6e50317c962240922747a4eb713f"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-win_amd64.whl", hash = "sha256:a6b85621fd3c81e31fc5f5add09c9c078b430db3595ca632efafdec9e64ebfaa"},
|
||||||
|
{file = "ruff-0.16.8-py3-none-win_arm64.whl", hash = "sha256:d075e820af612102ce217f07cc93e69f9490b10ec13ea85fa87bd03d996cef8a"},
|
||||||
|
{file = "ruff-0.16.8.tar.gz", hash = "sha256:9247bf92b5f04d825c8639a4fe423ec2e4222acd9222e58412b0dab7e442798b"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
@@ -217,4 +245,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.1"
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
content-hash = "c5fdfbbe6dbd3da1ba43276d267c57089584d9940904d4cd53d25ce9e9fcd1c9"
|
content-hash = "24c9a2d464f6c737eeae0a66f792fa23824c3960b7cc72a22fff1408f21ec812"
|
||||||
+18
-1
@@ -26,5 +26,22 @@ priority = "supplemental"
|
|||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest (>=9.1.1,<10.0.0)",
|
"pytest (>=9.1.1,<10.0.0)",
|
||||||
"pytest-expectdir (>=1.2.0,<2.0.0)"
|
"pytest-expectdir (>=1.2.0,<2.0.0)",
|
||||||
|
"ruff (>=0.16.8,<0.17.0)"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 79
|
||||||
|
target-version = "py311"
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = [
|
||||||
|
"E",
|
||||||
|
"F",
|
||||||
|
"W",
|
||||||
|
"I",
|
||||||
|
"UP"
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.format]
|
||||||
|
quote-style = "single"
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .cli.cli import main
|
from .cli.cli import main
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -12,8 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from . import cli
|
from . import cli, languages
|
||||||
from . import languages
|
|
||||||
from .cli import main
|
from .cli import main
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
@@ -12,13 +12,14 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
The command line interface
|
The command line interface
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .languages.python import VALID_SECTIONS as VALID_PYTHON_SECTIONS
|
from .languages.python import VALID_SECTIONS as VALID_PYTHON_SECTIONS
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -31,38 +32,39 @@ __all__ = [
|
|||||||
_LANG_PYTHON = 'python'
|
_LANG_PYTHON = 'python'
|
||||||
_PROGRAM_NAME = 'jcloud-docsgen'
|
_PROGRAM_NAME = 'jcloud-docsgen'
|
||||||
|
|
||||||
|
|
||||||
def existing_directory(path: str) -> pathlib.Path:
|
def existing_directory(path: str) -> pathlib.Path:
|
||||||
_path = path
|
_path = path
|
||||||
path = pathlib.Path(_path)
|
path = pathlib.Path(_path)
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
raise argparse.ArgumentTypeError(
|
raise argparse.ArgumentTypeError(f'{_path}: no such file or directory')
|
||||||
f'{_path}: no such file or directory'
|
|
||||||
)
|
|
||||||
if not path.is_dir():
|
if not path.is_dir():
|
||||||
raise argparse.ArgumentTypeError(f'{_path}: not a directory')
|
raise argparse.ArgumentTypeError(f'{_path}: not a directory')
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def error(message: str, exit_code: int = 1):
|
def error(message: str, exit_code: int = 1):
|
||||||
'''
|
"""
|
||||||
Shows an error message and returns the exit code.
|
Shows an error message and returns the exit code.
|
||||||
|
|
||||||
:param message: The error message to show.
|
:param message: The error message to show.
|
||||||
:type message: str
|
:type message: str
|
||||||
:param exit_code: The exit code.
|
:param exit_code: The exit code.
|
||||||
:type exit_code: int
|
:type exit_code: int
|
||||||
'''
|
"""
|
||||||
|
|
||||||
print(f'{_PROGRAM_NAME}: error: {message}', file=sys.stderr)
|
print(f'{_PROGRAM_NAME}: error: {message}', file=sys.stderr)
|
||||||
return exit_code
|
return exit_code
|
||||||
|
|
||||||
|
|
||||||
def create_argument_parser() -> argparse.ArgumentParser:
|
def create_argument_parser() -> argparse.ArgumentParser:
|
||||||
'''
|
"""
|
||||||
Prepares a parser to parse the command-line arguments using
|
Prepares a parser to parse the command-line arguments using
|
||||||
argparse.
|
argparse.
|
||||||
|
|
||||||
:return: The parser.
|
:return: The parser.
|
||||||
:rtype: argparse.ArgumentParser
|
:rtype: argparse.ArgumentParser
|
||||||
'''
|
"""
|
||||||
|
|
||||||
common_parser = argparse.ArgumentParser(add_help=False)
|
common_parser = argparse.ArgumentParser(add_help=False)
|
||||||
common_parser.add_argument(
|
common_parser.add_argument(
|
||||||
@@ -131,6 +133,7 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|||||||
|
|
||||||
return argument_parser
|
return argument_parser
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None) -> int:
|
def main(argv=None) -> int:
|
||||||
argument_parser = create_argument_parser()
|
argument_parser = create_argument_parser()
|
||||||
args = argument_parser.parse_args(argv)
|
args = argument_parser.parse_args(argv)
|
||||||
@@ -141,6 +144,7 @@ def main(argv = None) -> int:
|
|||||||
try:
|
try:
|
||||||
if args.language == _LANG_PYTHON:
|
if args.language == _LANG_PYTHON:
|
||||||
from .languages.python import generate
|
from .languages.python import generate
|
||||||
|
|
||||||
generate(
|
generate(
|
||||||
args.project_directory,
|
args.project_directory,
|
||||||
args.output_dir,
|
args.output_dir,
|
||||||
@@ -166,7 +170,7 @@ def main(argv = None) -> int:
|
|||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
# TODO: add a custom exception class to clearify that a project
|
# TODO: add a custom exception class to clearify that a project
|
||||||
# parsing syntax error is meant.
|
# parsing syntax error is meant.
|
||||||
return error(f'syntax error in project source code')
|
return error('syntax error in project source code')
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
return 130
|
return 130
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from ...core.python import (
|
from ...core.python import (
|
||||||
PythonDefinitionDocumentationIncludeSections,
|
PythonDefinitionDocumentationIncludeSections,
|
||||||
PythonDocumentationGenerator,
|
PythonDocumentationGenerator,
|
||||||
)
|
)
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'VALID_SECTIONS',
|
'VALID_SECTIONS',
|
||||||
@@ -29,24 +29,25 @@ VALID_SECTIONS = list(
|
|||||||
PythonDefinitionDocumentationIncludeSections()._args.keys()
|
PythonDefinitionDocumentationIncludeSections()._args.keys()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_sections_list(
|
def parse_sections_list(
|
||||||
include_sections: Optional[list[str]] = None,
|
include_sections: list[str] | None = None,
|
||||||
exclude_sections: list[str] = [],
|
exclude_sections: list[str] = [],
|
||||||
) -> PythonDefinitionDocumentationIncludeSections:
|
) -> PythonDefinitionDocumentationIncludeSections:
|
||||||
'''
|
"""
|
||||||
Returns a Python definition documentation sections list from the
|
Returns a Python definition documentation sections list from the
|
||||||
sections list to be included and the sections list to be excluded.
|
sections list to be included and the sections list to be excluded.
|
||||||
These lists both consist of the sections as strings which contain
|
These lists both consist of the sections as strings which contain
|
||||||
the section name.
|
the section name.
|
||||||
|
|
||||||
:param include_sections: The list of sections to be included.
|
:param include_sections: The list of sections to be included.
|
||||||
:type include_sections: Optional[list[str]]
|
:type include_sections: list[str] | None
|
||||||
:param exclude_sections: The list of sections to be excluded.
|
:param exclude_sections: The list of sections to be excluded.
|
||||||
:type exclude_sections: list[str]
|
:type exclude_sections: list[str]
|
||||||
|
|
||||||
:return: The Python definition documentation sections object.
|
:return: The Python definition documentation sections object.
|
||||||
:rtype: PythonDefinitionDocumentationIncludeSections
|
:rtype: PythonDefinitionDocumentationIncludeSections
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if include_sections is None:
|
if include_sections is None:
|
||||||
include_sections = VALID_SECTIONS[:]
|
include_sections = VALID_SECTIONS[:]
|
||||||
@@ -67,6 +68,7 @@ def parse_sections_list(
|
|||||||
class_body='class_body' in include_sections,
|
class_body='class_body' in include_sections,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _generate(
|
def _generate(
|
||||||
project_directory: pathlib.Path,
|
project_directory: pathlib.Path,
|
||||||
output_directory: pathlib.Path,
|
output_directory: pathlib.Path,
|
||||||
@@ -77,7 +79,7 @@ def _generate(
|
|||||||
allow_html: bool = False,
|
allow_html: bool = False,
|
||||||
allow_tables: bool = False,
|
allow_tables: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
'''
|
"""
|
||||||
Generates the documentation for a Python project.
|
Generates the documentation for a Python project.
|
||||||
|
|
||||||
:param project_directory: The project directory.
|
:param project_directory: The project directory.
|
||||||
@@ -92,7 +94,7 @@ def _generate(
|
|||||||
:type allow_html: bool
|
:type allow_html: bool
|
||||||
:param allow_tables: Indicates whether Markdown tables are allowed.
|
:param allow_tables: Indicates whether Markdown tables are allowed.
|
||||||
Warning: Markdown tables are not CommonMark.
|
Warning: Markdown tables are not CommonMark.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
docs_generator = PythonDocumentationGenerator(
|
docs_generator = PythonDocumentationGenerator(
|
||||||
project_directory,
|
project_directory,
|
||||||
@@ -103,16 +105,17 @@ def _generate(
|
|||||||
)
|
)
|
||||||
docs_generator.generate_documentation()
|
docs_generator.generate_documentation()
|
||||||
|
|
||||||
|
|
||||||
def generate(
|
def generate(
|
||||||
project_directory: pathlib.Path,
|
project_directory: pathlib.Path,
|
||||||
output_directory: pathlib.Path,
|
output_directory: pathlib.Path,
|
||||||
*,
|
*,
|
||||||
include_sections: Optional[list[str]] = None,
|
include_sections: list[str] | None = None,
|
||||||
exclude_sections: list[str] = [],
|
exclude_sections: list[str] = [],
|
||||||
allow_html: bool = False,
|
allow_html: bool = False,
|
||||||
allow_tables: bool = False,
|
allow_tables: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
'''
|
"""
|
||||||
Generates the documentation for a Python project.
|
Generates the documentation for a Python project.
|
||||||
|
|
||||||
:param project_directory: The project directory.
|
:param project_directory: The project directory.
|
||||||
@@ -121,7 +124,7 @@ def generate(
|
|||||||
:type output_directory: pathlib.Path
|
:type output_directory: pathlib.Path
|
||||||
:param include_sections: The sections to be included in the
|
:param include_sections: The sections to be included in the
|
||||||
documentation.
|
documentation.
|
||||||
:type include_sections: Optional[list[str]]
|
:type include_sections: list[str] | None
|
||||||
:param exclude_sections: The sections to be excluded in the
|
:param exclude_sections: The sections to be excluded in the
|
||||||
documentation.
|
documentation.
|
||||||
:type exclude_sections: list[str]
|
:type exclude_sections: list[str]
|
||||||
@@ -130,7 +133,7 @@ def generate(
|
|||||||
:type allow_html: bool
|
:type allow_html: bool
|
||||||
:param allow_tables: Indicates whether Markdown tables are allowed.
|
:param allow_tables: Indicates whether Markdown tables are allowed.
|
||||||
Warning: Markdown tables are not CommonMark.
|
Warning: Markdown tables are not CommonMark.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
sections = parse_sections_list(include_sections, exclude_sections)
|
sections = parse_sections_list(include_sections, exclude_sections)
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from . import namespaces
|
||||||
from ._core import *
|
from ._core import *
|
||||||
from ._core import __all__ as _core__all__
|
from ._core import __all__ as _core__all__
|
||||||
from . import namespaces
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
*_core__all__,
|
*_core__all__,
|
||||||
|
|||||||
@@ -13,25 +13,26 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from ...utils import non_empty_str, get_relative_path_segments
|
|
||||||
from ...exceptions import PythonArgumentStructureError, InvalidNamespaceError
|
import ast
|
||||||
from .namespaces import PythonModuleNamespace, PythonPackageNamespace
|
import pathlib
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import TypeAlias
|
||||||
|
|
||||||
|
import docstring_parser
|
||||||
|
|
||||||
|
from ...exceptions import InvalidNamespaceError, PythonArgumentStructureError
|
||||||
|
from ...utils import get_relative_path_segments, non_empty_str
|
||||||
|
from .arguments import PythonArgumentKind, PythonFunctionArgument
|
||||||
from .definitions import (
|
from .definitions import (
|
||||||
PythonAsyncFunctionDefinition,
|
PythonAsyncFunctionDefinition,
|
||||||
PythonClassDefinition,
|
PythonClassDefinition,
|
||||||
PythonDefinition,
|
PythonDefinition,
|
||||||
PythonFunctionDefinition,
|
PythonFunctionDefinition,
|
||||||
)
|
)
|
||||||
from .arguments import PythonArgumentKind, PythonFunctionArgument
|
|
||||||
from .docstrings import PythonDocstring
|
from .docstrings import PythonDocstring
|
||||||
from collections.abc import Iterator
|
from .namespaces import PythonModuleNamespace, PythonPackageNamespace
|
||||||
from typing import TypeAlias, Union
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from enum import Enum
|
|
||||||
import docstring_parser
|
|
||||||
import pathlib
|
|
||||||
import ast
|
|
||||||
import types
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'PythonDefinitionDocumentationIncludeSections',
|
'PythonDefinitionDocumentationIncludeSections',
|
||||||
@@ -40,15 +41,16 @@ __all__ = [
|
|||||||
'PythonDocumentationGenerator',
|
'PythonDocumentationGenerator',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _collect_definitions(tree_or_node) -> Iterator[PythonDefinition]:
|
def _collect_definitions(tree_or_node) -> Iterator[PythonDefinition]:
|
||||||
'''
|
"""
|
||||||
Collects all definitions of the tree or node.
|
Collects all definitions of the tree or node.
|
||||||
|
|
||||||
:param tree_or_node: The tree or node.
|
:param tree_or_node: The tree or node.
|
||||||
|
|
||||||
:return: The definitions.
|
:return: The definitions.
|
||||||
:rtype: Iterator[PythonDefinition]
|
:rtype: Iterator[PythonDefinition]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
for node in tree_or_node.body:
|
for node in tree_or_node.body:
|
||||||
if isinstance(node, ast.FunctionDef):
|
if isinstance(node, ast.FunctionDef):
|
||||||
@@ -58,18 +60,20 @@ def _collect_definitions(tree_or_node) -> Iterator[PythonDefinition]:
|
|||||||
if isinstance(node, ast.ClassDef):
|
if isinstance(node, ast.ClassDef):
|
||||||
yield PythonClassDefinition.from_node(node)
|
yield PythonClassDefinition.from_node(node)
|
||||||
|
|
||||||
|
|
||||||
_Arguments: TypeAlias = tuple[
|
_Arguments: TypeAlias = tuple[
|
||||||
list[PythonFunctionArgument],
|
list[PythonFunctionArgument],
|
||||||
list[PythonFunctionArgument],
|
list[PythonFunctionArgument],
|
||||||
Union[PythonFunctionArgument, None],
|
PythonFunctionArgument | None,
|
||||||
list[PythonFunctionArgument],
|
list[PythonFunctionArgument],
|
||||||
Union[PythonFunctionArgument, None],
|
PythonFunctionArgument | None,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _group_argument_list(
|
def _group_argument_list(
|
||||||
argument_list: list[PythonFunctionArgument],
|
argument_list: list[PythonFunctionArgument],
|
||||||
) -> _Arguments:
|
) -> _Arguments:
|
||||||
'''
|
"""
|
||||||
Groups the argument list into a list of positional-only arguments, a
|
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
|
list of normal arguments, the * argument if there is one, a list of
|
||||||
keyword-only arguments and the var-keyword-argument if there is one.
|
keyword-only arguments and the var-keyword-argument if there is one.
|
||||||
@@ -78,13 +82,12 @@ def _group_argument_list(
|
|||||||
:type argument_list: list[PythonFunctionArgument]
|
:type argument_list: list[PythonFunctionArgument]
|
||||||
|
|
||||||
:rtype: _Arguments
|
:rtype: _Arguments
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if argument_list:
|
if argument_list:
|
||||||
if argument_list[-1].kind != PythonArgumentKind.KWARGS and any([
|
if argument_list[-1].kind != PythonArgumentKind.KWARGS and any(
|
||||||
arg.kind == PythonArgumentKind.KWARGS
|
[arg.kind == PythonArgumentKind.KWARGS for arg in argument_list]
|
||||||
for arg in argument_list
|
):
|
||||||
]):
|
|
||||||
raise PythonArgumentStructureError(
|
raise PythonArgumentStructureError(
|
||||||
'arguments cannot follow var-keyword argument',
|
'arguments cannot follow var-keyword argument',
|
||||||
)
|
)
|
||||||
@@ -124,10 +127,11 @@ def _group_argument_list(
|
|||||||
kwargs,
|
kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _signature_argument_list(
|
def _signature_argument_list(
|
||||||
argument_list: list[PythonFunctionArgument],
|
argument_list: list[PythonFunctionArgument],
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
'''
|
"""
|
||||||
Returns a list of the arguments as strings for a function signature.
|
Returns a list of the arguments as strings for a function signature.
|
||||||
|
|
||||||
:param argument_list: The argument list.
|
:param argument_list: The argument list.
|
||||||
@@ -135,7 +139,7 @@ def _signature_argument_list(
|
|||||||
|
|
||||||
:return: The list of function signature argument strings.
|
:return: The list of function signature argument strings.
|
||||||
:rtype: list[str]
|
:rtype: list[str]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
(
|
(
|
||||||
positional_only_args,
|
positional_only_args,
|
||||||
@@ -170,8 +174,9 @@ def _signature_argument_list(
|
|||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
def _fit_row_length(expected: int, row: tuple) -> tuple:
|
def _fit_row_length(expected: int, row: tuple) -> tuple:
|
||||||
'''
|
"""
|
||||||
Fits the row length to the expected length.
|
Fits the row length to the expected length.
|
||||||
|
|
||||||
If the row is too long, the tail, that makes it too long, is omitted.
|
If the row is too long, the tail, that makes it too long, is omitted.
|
||||||
@@ -181,7 +186,7 @@ def _fit_row_length(expected: int, row: tuple) -> tuple:
|
|||||||
:type expected: int
|
:type expected: int
|
||||||
:param row: The row.
|
:param row: The row.
|
||||||
:type row: tuple
|
:type row: tuple
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if expected < 0:
|
if expected < 0:
|
||||||
raise ValueError('expected must be larger than or equal 0.')
|
raise ValueError('expected must be larger than or equal 0.')
|
||||||
@@ -191,6 +196,7 @@ def _fit_row_length(expected: int, row: tuple) -> tuple:
|
|||||||
else:
|
else:
|
||||||
return row + (None,) * (expected - len(row))
|
return row + (None,) * (expected - len(row))
|
||||||
|
|
||||||
|
|
||||||
def _markdown_table(
|
def _markdown_table(
|
||||||
header: tuple,
|
header: tuple,
|
||||||
body: list[tuple],
|
body: list[tuple],
|
||||||
@@ -200,7 +206,7 @@ def _markdown_table(
|
|||||||
one_line_values: bool = True,
|
one_line_values: bool = True,
|
||||||
missing_value: str = '--',
|
missing_value: str = '--',
|
||||||
) -> str:
|
) -> str:
|
||||||
'''
|
"""
|
||||||
Generates a table.
|
Generates a table.
|
||||||
|
|
||||||
If ``allow_markdown_table`` is ``False``, an alternative
|
If ``allow_markdown_table`` is ``False``, an alternative
|
||||||
@@ -222,7 +228,7 @@ def _markdown_table(
|
|||||||
:param missing_value: The string that is used if a value is missing
|
:param missing_value: The string that is used if a value is missing
|
||||||
(only if ``force_row_lengths`` is ``False``).
|
(only if ``force_row_lengths`` is ``False``).
|
||||||
:type missing_value: str
|
:type missing_value: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if not header or not body:
|
if not header or not body:
|
||||||
return ''
|
return ''
|
||||||
@@ -231,37 +237,34 @@ def _markdown_table(
|
|||||||
|
|
||||||
if allow_markdown_table:
|
if allow_markdown_table:
|
||||||
table = (
|
table = (
|
||||||
'| ' \
|
'| ' + ' | '.join(header) + ' |\n|' + ' --- |' * len(header) + '\n'
|
||||||
+ ' | '.join(header) \
|
|
||||||
+ ' |\n|' \
|
|
||||||
+ ' --- |' * len(header) \
|
|
||||||
+ '\n'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
for row in body:
|
for row in body:
|
||||||
if len(row) != header_len:
|
if len(row) != header_len:
|
||||||
if force_row_lengths:
|
if force_row_lengths:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f'expected {header_len} value{
|
f'expected {header_len} value{
|
||||||
"s"
|
"s" if header_len > 1 else ""
|
||||||
if header_len > 1
|
|
||||||
else ""
|
|
||||||
}, got {len(row)}',
|
}, got {len(row)}',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
row = _fit_row_length(header_len, row)
|
row = _fit_row_length(header_len, row)
|
||||||
|
|
||||||
table += '| ' + ' | '.join([
|
table += (
|
||||||
str(
|
'| '
|
||||||
col
|
+ ' | '.join(
|
||||||
if col is not None
|
[
|
||||||
else missing_value
|
str(col if col is not None else missing_value).replace(
|
||||||
).replace('\n', ' ')
|
'\n', ' '
|
||||||
|
)
|
||||||
if one_line_values
|
if one_line_values
|
||||||
else str(col if col is not None else missing_value)
|
else str(col if col is not None else missing_value)
|
||||||
for col in row
|
for col in row
|
||||||
]) + ' |\n'
|
]
|
||||||
|
)
|
||||||
|
+ ' |\n'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
table = ''
|
table = ''
|
||||||
|
|
||||||
@@ -270,9 +273,7 @@ def _markdown_table(
|
|||||||
if force_row_lengths:
|
if force_row_lengths:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f'expected {header_len} value{
|
f'expected {header_len} value{
|
||||||
"s"
|
"s" if header_len > 1 else ""
|
||||||
if header_len > 1
|
|
||||||
else ""
|
|
||||||
}, got {len(row)}',
|
}, got {len(row)}',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -281,37 +282,33 @@ def _markdown_table(
|
|||||||
sep = '- '
|
sep = '- '
|
||||||
for i, col in enumerate(row):
|
for i, col in enumerate(row):
|
||||||
table += (
|
table += (
|
||||||
sep \
|
sep
|
||||||
+ header[i] \
|
+ header[i]
|
||||||
+ ': ' \
|
+ ': '
|
||||||
+ (
|
+ (
|
||||||
str(
|
str(col if col is not None else missing_value).replace(
|
||||||
col
|
'\n', ' '
|
||||||
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'
|
if one_line_values
|
||||||
|
else str(col if col is not None else missing_value)
|
||||||
|
)
|
||||||
|
+ '\n\n'
|
||||||
)
|
)
|
||||||
sep = ' '
|
sep = ' '
|
||||||
|
|
||||||
return table.strip()
|
return table.strip()
|
||||||
|
|
||||||
def _is_docstring(docstring: Union[str, PythonDocstring, None]) -> bool:
|
|
||||||
'''
|
def _is_docstring(docstring: str | PythonDocstring | None) -> bool:
|
||||||
|
"""
|
||||||
Checks whether the docstring is a docstring or an empty docstring.
|
Checks whether the docstring is a docstring or an empty docstring.
|
||||||
|
|
||||||
:param docstring: The docstring.
|
:param docstring: The docstring.
|
||||||
:type docstring: Union[str, PythonDocstring, None]
|
:type docstring: str | PythonDocstring | None
|
||||||
|
|
||||||
:return: Whether there is a docstring.
|
:return: Whether there is a docstring.
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if docstring is None:
|
if docstring is None:
|
||||||
return False
|
return False
|
||||||
@@ -321,10 +318,12 @@ def _is_docstring(docstring: Union[str, PythonDocstring, None]) -> bool:
|
|||||||
|
|
||||||
return docstring.strip() != ''
|
return docstring.strip() != ''
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class _FunctionParamInfo:
|
class _FunctionParamInfo:
|
||||||
annotation: Union[str, None]
|
annotation: str | None
|
||||||
description: Union[str, None]
|
description: str | None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class _NoDocstringTemplate:
|
class _NoDocstringTemplate:
|
||||||
@@ -340,10 +339,12 @@ class _NoDocstringTemplate:
|
|||||||
long_description = None
|
long_description = None
|
||||||
blank_after_short_description = None
|
blank_after_short_description = None
|
||||||
|
|
||||||
|
|
||||||
_NoDocstring = _NoDocstringTemplate()
|
_NoDocstring = _NoDocstringTemplate()
|
||||||
|
|
||||||
|
|
||||||
def _argument_kind_human_readable(argument_kind: PythonArgumentKind) -> str:
|
def _argument_kind_human_readable(argument_kind: PythonArgumentKind) -> str:
|
||||||
'''
|
"""
|
||||||
Returns the human-readable representation of a
|
Returns the human-readable representation of a
|
||||||
``PythonArgumentKind``.
|
``PythonArgumentKind``.
|
||||||
|
|
||||||
@@ -352,15 +353,16 @@ def _argument_kind_human_readable(argument_kind: PythonArgumentKind) -> str:
|
|||||||
|
|
||||||
:return: The human-readable representation.
|
:return: The human-readable representation.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return argument_kind.name.lower().replace('_', '-')
|
return argument_kind.name.lower().replace('_', '-')
|
||||||
|
|
||||||
|
|
||||||
def _function_params_info(
|
def _function_params_info(
|
||||||
arg_list: list[PythonFunctionArgument],
|
arg_list: list[PythonFunctionArgument],
|
||||||
docstring_arg_list: list[docstring_parser.DocstringParam],
|
docstring_arg_list: list[docstring_parser.DocstringParam],
|
||||||
) -> dict[str, _FunctionParamInfo]:
|
) -> dict[str, _FunctionParamInfo]:
|
||||||
'''
|
"""
|
||||||
Returns a dictionary with the parameter info.
|
Returns a dictionary with the parameter info.
|
||||||
|
|
||||||
The keys of the dictionary are the parameter names and the values are
|
The keys of the dictionary are the parameter names and the values are
|
||||||
@@ -377,11 +379,10 @@ def _function_params_info(
|
|||||||
|
|
||||||
:return: The dictionary with the parameter types.
|
:return: The dictionary with the parameter types.
|
||||||
:rtype: dict[str, _FunctionParamInfo]
|
:rtype: dict[str, _FunctionParamInfo]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
arg_annotations = {
|
arg_annotations = {
|
||||||
arg.name: _FunctionParamInfo(arg.annotation, None)
|
arg.name: _FunctionParamInfo(arg.annotation, None) for arg in arg_list
|
||||||
for arg in arg_list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in docstring_arg_list:
|
for arg in docstring_arg_list:
|
||||||
@@ -393,8 +394,9 @@ def _function_params_info(
|
|||||||
|
|
||||||
return arg_annotations
|
return arg_annotations
|
||||||
|
|
||||||
|
|
||||||
class PythonDefinitionDocumentationIncludeSections:
|
class PythonDefinitionDocumentationIncludeSections:
|
||||||
'''
|
"""
|
||||||
A class representing the included sections in a Python definition
|
A class representing the included sections in a Python definition
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
@@ -414,7 +416,7 @@ class PythonDefinitionDocumentationIncludeSections:
|
|||||||
:type bases: bool
|
:type bases: bool
|
||||||
:param decorators: Whether include the decorators section.
|
:param decorators: Whether include the decorators section.
|
||||||
:type decorators: bool
|
:type decorators: bool
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -452,12 +454,17 @@ class PythonDefinitionDocumentationIncludeSections:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return type(self).__name__+ '(' + ', '.join(
|
return (
|
||||||
|
type(self).__name__
|
||||||
|
+ '('
|
||||||
|
+ ', '.join(
|
||||||
[
|
[
|
||||||
key + ' = ' + repr(value)
|
key + ' = ' + repr(value)
|
||||||
for key, value in self._args.items()
|
for key, value in self._args.items()
|
||||||
],
|
],
|
||||||
) + ')'
|
)
|
||||||
|
+ ')'
|
||||||
|
)
|
||||||
|
|
||||||
def __eq__(self, value) -> str:
|
def __eq__(self, value) -> str:
|
||||||
if not isinstance(
|
if not isinstance(
|
||||||
@@ -467,8 +474,9 @@ class PythonDefinitionDocumentationIncludeSections:
|
|||||||
return False
|
return False
|
||||||
return self._args == value._args
|
return self._args == value._args
|
||||||
|
|
||||||
|
|
||||||
class PythonDefinitionDocumentationGenerator:
|
class PythonDefinitionDocumentationGenerator:
|
||||||
'''
|
"""
|
||||||
A documentation generator for a Python definition.
|
A documentation generator for a Python definition.
|
||||||
|
|
||||||
:param definition: The Python definition.
|
:param definition: The Python definition.
|
||||||
@@ -492,7 +500,7 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
:type skip_empty_sections: bool
|
:type skip_empty_sections: bool
|
||||||
:param is_method: Controls whether the definition is a class method.
|
:param is_method: Controls whether the definition is a class method.
|
||||||
:type is_method: bool
|
:type is_method: bool
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -521,13 +529,15 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
@property
|
@property
|
||||||
def _docstring(self):
|
def _docstring(self):
|
||||||
if self._docstring_cache is None:
|
if self._docstring_cache is None:
|
||||||
self._docstring_cache = self.definition.doc.parse() \
|
self._docstring_cache = (
|
||||||
if _is_docstring(self.definition.doc) \
|
self.definition.doc.parse()
|
||||||
|
if _is_docstring(self.definition.doc)
|
||||||
else _NoDocstring
|
else _NoDocstring
|
||||||
|
)
|
||||||
return self._docstring_cache
|
return self._docstring_cache
|
||||||
|
|
||||||
def generate_signature(self) -> str:
|
def generate_signature(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the signature of the definition. Works only if the
|
Generates the signature of the definition. Works only if the
|
||||||
definition is a function or asynchronous function definition.
|
definition is a function or asynchronous function definition.
|
||||||
|
|
||||||
@@ -535,18 +545,23 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
|
|
||||||
:return: The signature.
|
:return: The signature.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if isinstance(self.definition, PythonClassDefinition):
|
if isinstance(self.definition, PythonClassDefinition):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
'Cannot generate a signature of a class definition.',
|
'Cannot generate a signature of a class definition.',
|
||||||
)
|
)
|
||||||
|
|
||||||
signature = self.definition.name + '(' + ', '.join(
|
signature = (
|
||||||
|
self.definition.name
|
||||||
|
+ '('
|
||||||
|
+ ', '.join(
|
||||||
_signature_argument_list(
|
_signature_argument_list(
|
||||||
self.definition.args,
|
self.definition.args,
|
||||||
),
|
),
|
||||||
) + ')'
|
)
|
||||||
|
+ ')'
|
||||||
|
)
|
||||||
|
|
||||||
if self.definition.returns is not None:
|
if self.definition.returns is not None:
|
||||||
signature += ' -> ' + self.definition.returns
|
signature += ' -> ' + self.definition.returns
|
||||||
@@ -554,38 +569,38 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return signature.strip()
|
return signature.strip()
|
||||||
|
|
||||||
def _generate_signature_section(self) -> str:
|
def _generate_signature_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the signature section.
|
Generates the signature section.
|
||||||
|
|
||||||
:return: The signature section.
|
:return: The signature section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
definition_type = 'Function' if not self.is_method else 'Method'
|
definition_type = 'Function' if not self.is_method else 'Method'
|
||||||
heading = self.level * '#' + '# ' + (definition_type) + ' signature'
|
heading = self.level * '#' + '# ' + (definition_type) + ' signature'
|
||||||
return f'''{heading}
|
return f"""{heading}
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{self.generate_signature()}
|
{self.generate_signature()}
|
||||||
```'''
|
```"""
|
||||||
|
|
||||||
def _generate_docstring_section(self) -> str:
|
def _generate_docstring_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the docstring section.
|
Generates the docstring section.
|
||||||
|
|
||||||
:return: The docstring section.
|
:return: The docstring section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
if _is_docstring(self.definition.doc) or not self.skip_empty_sections:
|
if _is_docstring(self.definition.doc) or not self.skip_empty_sections:
|
||||||
if self.allow_html:
|
if self.allow_html:
|
||||||
md += f'<details><summary><h{str(
|
md += f'<details><summary><h{
|
||||||
self.level + 1
|
str(self.level + 1)
|
||||||
)} style="display:inline">Docstring</h{str(
|
} style="display:inline">Docstring</h{
|
||||||
self.level + 1
|
str(self.level + 1)
|
||||||
)}></summary>'
|
}></summary>'
|
||||||
else:
|
else:
|
||||||
md += self.level * '#' + '# Docstring\n\n'
|
md += self.level * '#' + '# Docstring\n\n'
|
||||||
|
|
||||||
@@ -602,12 +617,12 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def _generate_params_section(self) -> str:
|
def _generate_params_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the parameters section.
|
Generates the parameters section.
|
||||||
|
|
||||||
:return: The parameters section.
|
:return: The parameters section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
@@ -647,12 +662,12 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def _generate_returns_section(self) -> str:
|
def _generate_returns_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the returns section.
|
Generates the returns section.
|
||||||
|
|
||||||
:return: The returns section.
|
:return: The returns section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
@@ -686,12 +701,12 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def _generate_exceptions_section(self) -> str:
|
def _generate_exceptions_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the exceptions section.
|
Generates the exceptions section.
|
||||||
|
|
||||||
:return: The exceptions section.
|
:return: The exceptions section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
@@ -699,26 +714,31 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
md += self.level * '#' + '# Exceptions\n\n'
|
md += self.level * '#' + '# Exceptions\n\n'
|
||||||
|
|
||||||
if self._docstring.raises:
|
if self._docstring.raises:
|
||||||
md += _markdown_table(('Exception', 'Description'), [
|
md += _markdown_table(
|
||||||
|
('Exception', 'Description'),
|
||||||
|
[
|
||||||
(
|
(
|
||||||
('`' + exception.type_name + '`')
|
('`' + exception.type_name + '`')
|
||||||
if exception.type_name is not None
|
if exception.type_name is not None
|
||||||
else None, non_empty_str(exception.description)
|
else None,
|
||||||
|
non_empty_str(exception.description),
|
||||||
)
|
)
|
||||||
for exception in self._docstring.raises
|
for exception in self._docstring.raises
|
||||||
], allow_markdown_table = self.allow_tables)
|
],
|
||||||
|
allow_markdown_table=self.allow_tables,
|
||||||
|
)
|
||||||
elif not self.skip_empty_sections:
|
elif not self.skip_empty_sections:
|
||||||
md += 'This function raises no exceptions.'
|
md += 'This function raises no exceptions.'
|
||||||
|
|
||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def _generate_examples_section(self) -> str:
|
def _generate_examples_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the examples section.
|
Generates the examples section.
|
||||||
|
|
||||||
:return: The examples section.
|
:return: The examples section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
@@ -727,9 +747,11 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
|
|
||||||
examples = []
|
examples = []
|
||||||
for example in self._docstring.examples:
|
for example in self._docstring.examples:
|
||||||
examples.append('```python\n' +\
|
examples.append(
|
||||||
(example.snippet or example.description) + \
|
'```python\n'
|
||||||
'\n```')
|
+ (example.snippet or example.description)
|
||||||
|
+ '\n```'
|
||||||
|
)
|
||||||
md += '\n\n---\n\n'.join(examples)
|
md += '\n\n---\n\n'.join(examples)
|
||||||
|
|
||||||
if not self._docstring.examples and not self.skip_empty_sections:
|
if not self._docstring.examples and not self.skip_empty_sections:
|
||||||
@@ -738,12 +760,12 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def _generate_decorators_section(self) -> str:
|
def _generate_decorators_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the decorators section.
|
Generates the decorators section.
|
||||||
|
|
||||||
:return: The decorators section.
|
:return: The decorators section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
@@ -759,12 +781,12 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def _generate_bases_section(self) -> str:
|
def _generate_bases_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the bases section.
|
Generates the bases section.
|
||||||
|
|
||||||
:return: The bases section.
|
:return: The bases section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
@@ -780,12 +802,12 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def _generate_body_section(self) -> str:
|
def _generate_body_section(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the body section.
|
Generates the body section.
|
||||||
|
|
||||||
:return: The body section.
|
:return: The body section.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = ''
|
md = ''
|
||||||
|
|
||||||
@@ -793,7 +815,8 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
md += self.level * '#' + '# Body\n\n'
|
md += self.level * '#' + '# Body\n\n'
|
||||||
|
|
||||||
for method_or_class in self.definition.body:
|
for method_or_class in self.definition.body:
|
||||||
md += PythonDefinitionDocumentationGenerator(
|
md += (
|
||||||
|
PythonDefinitionDocumentationGenerator(
|
||||||
method_or_class,
|
method_or_class,
|
||||||
self.include_sections,
|
self.include_sections,
|
||||||
level=self.level + 2,
|
level=self.level + 2,
|
||||||
@@ -801,7 +824,9 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
allow_tables=self.allow_tables,
|
allow_tables=self.allow_tables,
|
||||||
skip_empty_sections=self.skip_empty_sections,
|
skip_empty_sections=self.skip_empty_sections,
|
||||||
is_method=True,
|
is_method=True,
|
||||||
).generate_documentation() + '\n\n'
|
).generate_documentation()
|
||||||
|
+ '\n\n'
|
||||||
|
)
|
||||||
|
|
||||||
if not self.definition.body and not self.skip_empty_sections:
|
if not self.definition.body and not self.skip_empty_sections:
|
||||||
md += 'This class does not have a body.'
|
md += 'This class does not have a body.'
|
||||||
@@ -809,12 +834,12 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
def generate_documentation(self) -> str:
|
def generate_documentation(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the documentation in the markdown format.
|
Generates the documentation in the markdown format.
|
||||||
|
|
||||||
:return: The documentation.
|
:return: The documentation.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
md = self.level * '#' + ' '
|
md = self.level * '#' + ' '
|
||||||
|
|
||||||
@@ -874,8 +899,9 @@ class PythonDefinitionDocumentationGenerator:
|
|||||||
|
|
||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
|
|
||||||
class PythonModuleDocumentationGenerator:
|
class PythonModuleDocumentationGenerator:
|
||||||
'''
|
"""
|
||||||
A documentation generator for a Python module.
|
A documentation generator for a Python module.
|
||||||
|
|
||||||
:param module_path: The path of the module file.
|
:param module_path: The path of the module file.
|
||||||
@@ -883,16 +909,16 @@ class PythonModuleDocumentationGenerator:
|
|||||||
:param src_directory: The directory of the source code. It is
|
:param src_directory: The directory of the source code. It is
|
||||||
optional and only used for making the module
|
optional and only used for making the module
|
||||||
identifiers in the documentation relative.
|
identifiers in the documentation relative.
|
||||||
:type src_directory: Union[pathlib.Path, None]
|
:type src_directory: pathlib.Path | None
|
||||||
:param include_sections: The sections that will be included.
|
:param include_sections: The sections that will be included.
|
||||||
:type include_sections: PythonDefinitionDocumentationIncludeSections
|
:type include_sections: PythonDefinitionDocumentationIncludeSections
|
||||||
:param kwargs: The options for ``PythonDefinitionDocumentationGenerator``.
|
:param kwargs: The options for ``PythonDefinitionDocumentationGenerator``.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
module_path: pathlib.Path,
|
module_path: pathlib.Path,
|
||||||
src_directory: Union[pathlib.Path, None] = None,
|
src_directory: pathlib.Path | None = None,
|
||||||
include_sections: PythonDefinitionDocumentationIncludeSections = (
|
include_sections: PythonDefinitionDocumentationIncludeSections = (
|
||||||
PythonDefinitionDocumentationIncludeSections()
|
PythonDefinitionDocumentationIncludeSections()
|
||||||
),
|
),
|
||||||
@@ -930,25 +956,24 @@ class PythonModuleDocumentationGenerator:
|
|||||||
return self._ast_cache
|
return self._ast_cache
|
||||||
|
|
||||||
def collect_definitions(self) -> Iterator[PythonDefinition]:
|
def collect_definitions(self) -> Iterator[PythonDefinition]:
|
||||||
'''
|
"""
|
||||||
Collects all function and class definitions of the module.
|
Collects all function and class definitions of the module.
|
||||||
|
|
||||||
:return: All definitions of the module
|
:return: All definitions of the module
|
||||||
:rtype: Iterator[PythonDefinition]
|
:rtype: Iterator[PythonDefinition]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
tree = self._ast
|
tree = self._ast
|
||||||
|
|
||||||
return _collect_definitions(tree)
|
return _collect_definitions(tree)
|
||||||
|
|
||||||
def generate_documentation(self) -> str:
|
def generate_documentation(self) -> str:
|
||||||
'''
|
"""
|
||||||
Generates the documentation in the markdown format.
|
Generates the documentation in the markdown format.
|
||||||
|
|
||||||
:return: The documentation.
|
:return: The documentation.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
md = '# Module `'
|
md = '# Module `'
|
||||||
|
|
||||||
@@ -973,15 +998,19 @@ class PythonModuleDocumentationGenerator:
|
|||||||
md += docstring + '\n\n'
|
md += docstring + '\n\n'
|
||||||
|
|
||||||
for definition in self.collect_definitions():
|
for definition in self.collect_definitions():
|
||||||
md += PythonDefinitionDocumentationGenerator(
|
md += (
|
||||||
|
PythonDefinitionDocumentationGenerator(
|
||||||
definition,
|
definition,
|
||||||
self.include_sections,
|
self.include_sections,
|
||||||
2,
|
2,
|
||||||
**self.kwargs,
|
**self.kwargs,
|
||||||
).generate_documentation() + '\n\n'
|
).generate_documentation()
|
||||||
|
+ '\n\n'
|
||||||
|
)
|
||||||
|
|
||||||
return md.strip()
|
return md.strip()
|
||||||
|
|
||||||
|
|
||||||
def _generate_package_namespace_documentation(
|
def _generate_package_namespace_documentation(
|
||||||
namespace: PythonPackageNamespace,
|
namespace: PythonPackageNamespace,
|
||||||
documentation_directory: pathlib.Path,
|
documentation_directory: pathlib.Path,
|
||||||
@@ -992,7 +1021,7 @@ def _generate_package_namespace_documentation(
|
|||||||
),
|
),
|
||||||
kwargs: dict = {},
|
kwargs: dict = {},
|
||||||
) -> None:
|
) -> None:
|
||||||
'''
|
"""
|
||||||
Generates the documentation for a package namespace and writes it to
|
Generates the documentation for a package namespace and writes it to
|
||||||
the documentation directory.
|
the documentation directory.
|
||||||
|
|
||||||
@@ -1007,19 +1036,20 @@ def _generate_package_namespace_documentation(
|
|||||||
:param include_sections: The sections to be included in the documentation.
|
:param include_sections: The sections to be included in the documentation.
|
||||||
:type include_sections: PythonDefinitionDocumentationIncludeSections
|
:type include_sections: PythonDefinitionDocumentationIncludeSections
|
||||||
:param kwargs: The options for the documentation generator.
|
:param kwargs: The options for the documentation generator.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
documentation_directory = pathlib.Path(str(documentation_directory))
|
documentation_directory = pathlib.Path(str(documentation_directory))
|
||||||
|
|
||||||
for sub_namespace in namespace.sub_namespaces:
|
for sub_namespace in namespace.sub_namespaces:
|
||||||
if isinstance(sub_namespace, PythonPackageNamespace):
|
if isinstance(sub_namespace, PythonPackageNamespace):
|
||||||
(
|
(documentation_directory / sub_namespace.name).mkdir(
|
||||||
documentation_directory / sub_namespace.name
|
mode=0o700, exist_ok=True
|
||||||
).mkdir(mode = 0o700, exist_ok = True)
|
)
|
||||||
_generate_package_namespace_documentation(
|
_generate_package_namespace_documentation(
|
||||||
sub_namespace,
|
sub_namespace,
|
||||||
documentation_directory / sub_namespace.name,
|
documentation_directory / sub_namespace.name,
|
||||||
root_directory, dir / sub_namespace.name,
|
root_directory,
|
||||||
|
dir / sub_namespace.name,
|
||||||
include_sections,
|
include_sections,
|
||||||
kwargs,
|
kwargs,
|
||||||
)
|
)
|
||||||
@@ -1035,8 +1065,9 @@ def _generate_package_namespace_documentation(
|
|||||||
).generate_documentation()
|
).generate_documentation()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PythonDocumentationGenerator:
|
class PythonDocumentationGenerator:
|
||||||
'''
|
"""
|
||||||
The class for the documentation generator.
|
The class for the documentation generator.
|
||||||
|
|
||||||
:param project_directory: The directory of the project.
|
:param project_directory: The directory of the project.
|
||||||
@@ -1046,7 +1077,7 @@ class PythonDocumentationGenerator:
|
|||||||
:param include_sections: The sections to be included in the documentation.
|
:param include_sections: The sections to be included in the documentation.
|
||||||
:type include_sections: PythonDefinitionDocumentationIncludeSections
|
:type include_sections: PythonDefinitionDocumentationIncludeSections
|
||||||
:param kwargs: The options for ``PythonDefinitionDocumentationGenerator``.
|
:param kwargs: The options for ``PythonDefinitionDocumentationGenerator``.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -1074,25 +1105,24 @@ class PythonDocumentationGenerator:
|
|||||||
return namespace
|
return namespace
|
||||||
|
|
||||||
def namespace(self) -> list[PythonPackageNamespace]:
|
def namespace(self) -> list[PythonPackageNamespace]:
|
||||||
'''
|
"""
|
||||||
Returns the project as a namespace.
|
Returns the project as a namespace.
|
||||||
|
|
||||||
:return: The project as a namespace.
|
:return: The project as a namespace.
|
||||||
:rtype: list[PythonPackageNamespace]
|
:rtype: list[PythonPackageNamespace]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
src_dir = self.project_directory / 'src'
|
src_dir = self.project_directory / 'src'
|
||||||
|
|
||||||
return [
|
return [
|
||||||
self._namespace(dir)
|
self._namespace(dir) for dir in src_dir.iterdir() if dir.is_dir()
|
||||||
for dir in src_dir.iterdir() if dir.is_dir()
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def generate_documentation(self) -> None:
|
def generate_documentation(self) -> None:
|
||||||
'''
|
"""
|
||||||
Generates the full project documentation and writes it to the
|
Generates the full project documentation and writes it to the
|
||||||
documentation directory.
|
documentation directory.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
_generate_package_namespace_documentation(
|
_generate_package_namespace_documentation(
|
||||||
PythonPackageNamespace('src', self.namespace()),
|
PythonPackageNamespace('src', self.namespace()),
|
||||||
|
|||||||
@@ -13,17 +13,18 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from enum import Enum
|
|
||||||
from typing import Union
|
|
||||||
import ast
|
import ast
|
||||||
from ...utils import assert_that_is_instance
|
import keyword
|
||||||
|
import types
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from ...exceptions import (
|
from ...exceptions import (
|
||||||
InvalidPythonAnnotationError,
|
InvalidPythonAnnotationError,
|
||||||
InvalidPythonIdentifierError,
|
InvalidPythonIdentifierError,
|
||||||
PythonArgumentStructureError,
|
PythonArgumentStructureError,
|
||||||
)
|
)
|
||||||
import keyword
|
from ...utils import assert_that_is_instance
|
||||||
import types
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'PythonArgumentKind',
|
'PythonArgumentKind',
|
||||||
@@ -31,6 +32,7 @@ __all__ = [
|
|||||||
'PythonASTArgumentsListParser',
|
'PythonASTArgumentsListParser',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PythonArgumentKind(Enum):
|
class PythonArgumentKind(Enum):
|
||||||
NORMAL = 0
|
NORMAL = 0
|
||||||
POSITIONAL_ONLY = 1
|
POSITIONAL_ONLY = 1
|
||||||
@@ -38,8 +40,9 @@ class PythonArgumentKind(Enum):
|
|||||||
VARARG = 3
|
VARARG = 3
|
||||||
KWARGS = 4
|
KWARGS = 4
|
||||||
|
|
||||||
|
|
||||||
class PythonFunctionArgument:
|
class PythonFunctionArgument:
|
||||||
'''
|
"""
|
||||||
Represents an argument of a Python function.
|
Represents an argument of a Python function.
|
||||||
|
|
||||||
:param name: The name of the argument.
|
:param name: The name of the argument.
|
||||||
@@ -47,17 +50,17 @@ class PythonFunctionArgument:
|
|||||||
:param kind: The kind of the argument.
|
:param kind: The kind of the argument.
|
||||||
:type kind: ArgumentKind
|
:type kind: ArgumentKind
|
||||||
:param default: The default value of the argument.
|
:param default: The default value of the argument.
|
||||||
:type default: Union[str, None]
|
:type default: str | None
|
||||||
:param annotation: The type annotation of the argument.
|
:param annotation: The type annotation of the argument.
|
||||||
:type annotation: Union[str, None]
|
:type annotation: str | None
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
kind: PythonArgumentKind,
|
kind: PythonArgumentKind,
|
||||||
default: Union[str, None],
|
default: str | None,
|
||||||
annotation: Union[str, None],
|
annotation: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
assert_that_is_instance(name, str)
|
assert_that_is_instance(name, str)
|
||||||
assert_that_is_instance(kind, PythonArgumentKind)
|
assert_that_is_instance(kind, PythonArgumentKind)
|
||||||
@@ -85,10 +88,14 @@ class PythonFunctionArgument:
|
|||||||
self.default = default
|
self.default = default
|
||||||
self.annotation = annotation
|
self.annotation = annotation
|
||||||
|
|
||||||
if self.kind in (
|
if (
|
||||||
|
self.kind
|
||||||
|
in (
|
||||||
PythonArgumentKind.VARARG,
|
PythonArgumentKind.VARARG,
|
||||||
PythonArgumentKind.KWARGS,
|
PythonArgumentKind.KWARGS,
|
||||||
) and self.default is not None:
|
)
|
||||||
|
and self.default is not None
|
||||||
|
):
|
||||||
raise PythonArgumentStructureError(
|
raise PythonArgumentStructureError(
|
||||||
'* or ** arguments cannot have default values',
|
'* or ** arguments cannot have default values',
|
||||||
definition=self.signature_repr(),
|
definition=self.signature_repr(),
|
||||||
@@ -105,20 +112,22 @@ class PythonFunctionArgument:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return type(self).__name__ + repr((
|
return type(self).__name__ + repr(
|
||||||
|
(
|
||||||
self.name,
|
self.name,
|
||||||
self.kind,
|
self.kind,
|
||||||
self.default,
|
self.default,
|
||||||
self.annotation,
|
self.annotation,
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def signature_repr(self) -> str:
|
def signature_repr(self) -> str:
|
||||||
'''
|
"""
|
||||||
Returns the function signature representation of the argument.
|
Returns the function signature representation of the argument.
|
||||||
|
|
||||||
:return: The string representation of the argument.
|
:return: The string representation of the argument.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if self.kind == PythonArgumentKind.KWARGS:
|
if self.kind == PythonArgumentKind.KWARGS:
|
||||||
str_repr = '**'
|
str_repr = '**'
|
||||||
@@ -137,48 +146,44 @@ class PythonFunctionArgument:
|
|||||||
|
|
||||||
return str_repr
|
return str_repr
|
||||||
|
|
||||||
|
|
||||||
class PythonASTArgumentsListParser:
|
class PythonASTArgumentsListParser:
|
||||||
'''
|
"""
|
||||||
A parser for making ``PythonFunctionArgument`` objects from an
|
A parser for making ``PythonFunctionArgument`` objects from an
|
||||||
``ast.arguments`` arguments list.
|
``ast.arguments`` arguments list.
|
||||||
|
|
||||||
:param ast_arguments_list: The ``ast.arguments`` arguments list.
|
:param ast_arguments_list: The ``ast.arguments`` arguments list.
|
||||||
:type ast_arguments_list: ast.arguments
|
:type ast_arguments_list: ast.arguments
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, ast_arguments_list: ast.arguments) -> None:
|
def __init__(self, ast_arguments_list: ast.arguments) -> None:
|
||||||
self.ast_arguments_list = ast_arguments_list
|
self.ast_arguments_list = ast_arguments_list
|
||||||
|
|
||||||
def to_argument_list(self) -> list[PythonFunctionArgument]:
|
def to_argument_list(self) -> list[PythonFunctionArgument]:
|
||||||
'''
|
"""
|
||||||
Converts the AST arguments list to a list of Python function
|
Converts the AST arguments list to a list of Python function
|
||||||
argument objects.
|
argument objects.
|
||||||
|
|
||||||
:return: The list of Python function argument objects.
|
:return: The list of Python function argument objects.
|
||||||
:rtype: list[PythonFunctionArgument]
|
:rtype: list[PythonFunctionArgument]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
defaults = (
|
defaults = (
|
||||||
[
|
[
|
||||||
None
|
None
|
||||||
for _ in range(len(
|
for _ in range(
|
||||||
self.ast_arguments_list.posonlyargs +
|
len(
|
||||||
self.ast_arguments_list.args +
|
self.ast_arguments_list.posonlyargs
|
||||||
self.ast_arguments_list.kwonlyargs
|
+ self.ast_arguments_list.args
|
||||||
) - len(
|
+ self.ast_arguments_list.kwonlyargs
|
||||||
self.ast_arguments_list.defaults
|
)
|
||||||
) - len(
|
- len(self.ast_arguments_list.defaults)
|
||||||
self.ast_arguments_list.kw_defaults
|
- len(self.ast_arguments_list.kw_defaults)
|
||||||
))
|
)
|
||||||
]
|
]
|
||||||
|
+ [ast.unparse(d) for d in self.ast_arguments_list.defaults]
|
||||||
+ [
|
+ [
|
||||||
ast.unparse(d)
|
ast.unparse(d) if d is not None else None
|
||||||
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
|
for d in self.ast_arguments_list.kw_defaults
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,14 +13,15 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Union
|
|
||||||
import ast
|
import ast
|
||||||
from .arguments import PythonFunctionArgument, PythonASTArgumentsListParser
|
|
||||||
from .docstrings import PythonDocstring
|
|
||||||
import keyword
|
import keyword
|
||||||
|
import types
|
||||||
|
|
||||||
from ...exceptions import InvalidPythonIdentifierError
|
from ...exceptions import InvalidPythonIdentifierError
|
||||||
from ...utils import assert_that_is_instance
|
from ...utils import assert_that_is_instance
|
||||||
import types
|
from .arguments import PythonASTArgumentsListParser, PythonFunctionArgument
|
||||||
|
from .docstrings import PythonDocstring
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'PythonDefinition',
|
'PythonDefinition',
|
||||||
@@ -29,40 +30,42 @@ __all__ = [
|
|||||||
'PythonClassDefinition',
|
'PythonClassDefinition',
|
||||||
]
|
]
|
||||||
|
|
||||||
def _python_docstring(doc: Union[str, None]) -> Union[PythonDocstring, None]:
|
|
||||||
'''
|
def _python_docstring(doc: str | None) -> PythonDocstring | None:
|
||||||
|
"""
|
||||||
Returns the PythonDocstring object if the docstring is a string and
|
Returns the PythonDocstring object if the docstring is a string and
|
||||||
not ``None``.
|
not ``None``.
|
||||||
|
|
||||||
:param doc: The docstring.
|
:param doc: The docstring.
|
||||||
:type doc: Union[str, None]
|
:type doc: str | None
|
||||||
|
|
||||||
:return: The PythonDocstring object or ``None``.
|
:return: The PythonDocstring object or ``None``.
|
||||||
:rtype: Union[PythonDocstring, None]
|
:rtype: PythonDocstring | None
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return PythonDocstring(doc) if doc is not None else None
|
return PythonDocstring(doc) if doc is not None else None
|
||||||
|
|
||||||
|
|
||||||
class PythonDefinition:
|
class PythonDefinition:
|
||||||
'''
|
"""
|
||||||
A base class for Python definition (a class, function or asynchronous
|
A base class for Python definition (a class, function or asynchronous
|
||||||
function definition) classes.
|
function definition) classes.
|
||||||
|
|
||||||
:param name: The name of the definition.
|
:param name: The name of the definition.
|
||||||
:type name: str
|
:type name: str
|
||||||
:param doc: The docstring.
|
:param doc: The docstring.
|
||||||
:type doc: Union[PythonDocstring, None]
|
:type doc: PythonDocstring | None
|
||||||
:param decorators: The definition decorators.
|
:param decorators: The definition decorators.
|
||||||
:type decorators: list[str]
|
:type decorators: list[str]
|
||||||
|
|
||||||
:raises InvalidPythonIdentifierError: If the name is not a valid
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
||||||
Python identifier.
|
Python identifier.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
doc: Union[PythonDocstring, None],
|
doc: PythonDocstring | None,
|
||||||
decorators: list[str],
|
decorators: list[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
if keyword.iskeyword(name) or not name.isidentifier() or not name:
|
if keyword.iskeyword(name) or not name.isidentifier() or not name:
|
||||||
@@ -89,12 +92,12 @@ class PythonDefinition:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_node(cls, node: ast.stmt) -> PythonDefinition:
|
def from_node(cls, node: ast.stmt) -> PythonDefinition:
|
||||||
'''
|
"""
|
||||||
From an AST node.
|
From an AST node.
|
||||||
|
|
||||||
:param node: The AST node.
|
:param node: The AST node.
|
||||||
:type node: ast.stmt
|
:type node: ast.stmt
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
node.name,
|
node.name,
|
||||||
@@ -107,8 +110,9 @@ class PythonDefinition:
|
|||||||
return False
|
return False
|
||||||
return self._arg_list() == value._arg_list()
|
return self._arg_list() == value._arg_list()
|
||||||
|
|
||||||
|
|
||||||
class PythonFunctionDefinition(PythonDefinition):
|
class PythonFunctionDefinition(PythonDefinition):
|
||||||
'''
|
"""
|
||||||
A PythonDefinition subclass representing a Python function
|
A PythonDefinition subclass representing a Python function
|
||||||
definition.
|
definition.
|
||||||
|
|
||||||
@@ -117,22 +121,22 @@ class PythonFunctionDefinition(PythonDefinition):
|
|||||||
:param args: The arguments.
|
:param args: The arguments.
|
||||||
:type args: list[PythonFunctionArgument]
|
:type args: list[PythonFunctionArgument]
|
||||||
:param returns: The return type of the function or class.
|
:param returns: The return type of the function or class.
|
||||||
:type returns: Union[str, None]
|
:type returns: str | None
|
||||||
:param doc: The docstring.
|
:param doc: The docstring.
|
||||||
:type doc: Union[PythonDocstring, None]
|
:type doc: PythonDocstring | None
|
||||||
:param decorators: The definition decorators.
|
:param decorators: The definition decorators.
|
||||||
:type decorators: list[str]
|
:type decorators: list[str]
|
||||||
|
|
||||||
:raises InvalidPythonIdentifierError: If the name is not a valid
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
||||||
Python identifier.
|
Python identifier.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
args: list[PythonFunctionArgument],
|
args: list[PythonFunctionArgument],
|
||||||
returns: Union[str, None],
|
returns: str | None,
|
||||||
doc: Union[PythonDocstring, None],
|
doc: PythonDocstring | None,
|
||||||
decorators: list[str],
|
decorators: list[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(name, doc, decorators)
|
super().__init__(name, doc, decorators)
|
||||||
@@ -150,12 +154,12 @@ class PythonFunctionDefinition(PythonDefinition):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_node(cls, node: ast.stmt) -> PythonFunctionDefinition:
|
def from_node(cls, node: ast.stmt) -> PythonFunctionDefinition:
|
||||||
'''
|
"""
|
||||||
From an AST node.
|
From an AST node.
|
||||||
|
|
||||||
:param node: The AST node.
|
:param node: The AST node.
|
||||||
:type node: ast.stmt
|
:type node: ast.stmt
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
node.name,
|
node.name,
|
||||||
@@ -165,8 +169,9 @@ class PythonFunctionDefinition(PythonDefinition):
|
|||||||
[ast.unparse(d) for d in node.decorator_list],
|
[ast.unparse(d) for d in node.decorator_list],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PythonAsyncFunctionDefinition(PythonFunctionDefinition):
|
class PythonAsyncFunctionDefinition(PythonFunctionDefinition):
|
||||||
'''
|
"""
|
||||||
A PythonDefinition subclass representing an asynchronous Python function
|
A PythonDefinition subclass representing an asynchronous Python function
|
||||||
definition.
|
definition.
|
||||||
|
|
||||||
@@ -175,18 +180,19 @@ class PythonAsyncFunctionDefinition(PythonFunctionDefinition):
|
|||||||
:param args: The arguments.
|
:param args: The arguments.
|
||||||
:type args: list[PythonFunctionArgument]
|
:type args: list[PythonFunctionArgument]
|
||||||
:param returns: The return type of the function or class.
|
:param returns: The return type of the function or class.
|
||||||
:type returns: Union[str, None]
|
:type returns: str | None
|
||||||
:param doc: The docstring.
|
:param doc: The docstring.
|
||||||
:type doc: Union[PythonDocstring, None]
|
:type doc: PythonDocstring | None
|
||||||
:param decorators: The definition decorators.
|
:param decorators: The definition decorators.
|
||||||
:type decorators: list[str]
|
:type decorators: list[str]
|
||||||
|
|
||||||
:raises InvalidPythonIdentifierError: If the name is not a valid
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
||||||
Python identifier.
|
Python identifier.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
class PythonClassDefinition(PythonDefinition):
|
class PythonClassDefinition(PythonDefinition):
|
||||||
'''
|
"""
|
||||||
A PythonDefinition subclass representing a Python class definition.
|
A PythonDefinition subclass representing a Python class definition.
|
||||||
|
|
||||||
:param name: The name of the definition.
|
:param name: The name of the definition.
|
||||||
@@ -194,7 +200,7 @@ class PythonClassDefinition(PythonDefinition):
|
|||||||
:param bases: The base classes.
|
:param bases: The base classes.
|
||||||
:type bases: list[str]
|
:type bases: list[str]
|
||||||
:param doc: The docstring.
|
:param doc: The docstring.
|
||||||
:type doc: Union[PythonDocstring, None]
|
:type doc: PythonDocstring | None
|
||||||
:param decorators: The definition decorators.
|
:param decorators: The definition decorators.
|
||||||
:type decorators: list[str]
|
:type decorators: list[str]
|
||||||
:param body:
|
:param body:
|
||||||
@@ -202,13 +208,13 @@ class PythonClassDefinition(PythonDefinition):
|
|||||||
|
|
||||||
:raises InvalidPythonIdentifierError: If the name is not a valid
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
||||||
Python identifier.
|
Python identifier.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
bases: list[str],
|
bases: list[str],
|
||||||
doc: Union[PythonDocstring, None],
|
doc: PythonDocstring | None,
|
||||||
decorators: list[str],
|
decorators: list[str],
|
||||||
body: list[PythonDefinition],
|
body: list[PythonDefinition],
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -227,12 +233,12 @@ class PythonClassDefinition(PythonDefinition):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_node(cls, node: ast.stmt) -> PythonClassDefinition:
|
def from_node(cls, node: ast.stmt) -> PythonClassDefinition:
|
||||||
'''
|
"""
|
||||||
From an AST node.
|
From an AST node.
|
||||||
|
|
||||||
:param node: The AST node.
|
:param node: The AST node.
|
||||||
:type node: ast.stmt
|
:type node: ast.stmt
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
node.name,
|
node.name,
|
||||||
@@ -242,16 +248,12 @@ class PythonClassDefinition(PythonDefinition):
|
|||||||
[
|
[
|
||||||
PythonFunctionDefinition.from_node(expr)
|
PythonFunctionDefinition.from_node(expr)
|
||||||
if isinstance(expr, ast.FunctionDef)
|
if isinstance(expr, ast.FunctionDef)
|
||||||
|
|
||||||
else PythonAsyncFunctionDefinition.from_node(expr)
|
else PythonAsyncFunctionDefinition.from_node(expr)
|
||||||
if isinstance(expr, ast.AsyncFunctionDef)
|
if isinstance(expr, ast.AsyncFunctionDef)
|
||||||
|
|
||||||
else PythonClassDefinition.from_node(expr)
|
else PythonClassDefinition.from_node(expr)
|
||||||
|
|
||||||
for expr in node.body
|
for expr in node.body
|
||||||
if isinstance(
|
if isinstance(
|
||||||
expr,
|
expr, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)
|
||||||
(ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -12,20 +12,18 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
import docstring_parser
|
import docstring_parser
|
||||||
|
|
||||||
__all__ = [
|
__all__ = ['PythonDocstring']
|
||||||
'PythonDocstring'
|
|
||||||
]
|
|
||||||
|
|
||||||
class PythonDocstring:
|
class PythonDocstring:
|
||||||
'''
|
"""
|
||||||
Represents a Python docstring.
|
Represents a Python docstring.
|
||||||
|
|
||||||
:param docstring: The docstring as a string.
|
:param docstring: The docstring as a string.
|
||||||
:type docstring: str
|
:type docstring: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, docstring: str) -> None:
|
def __init__(self, docstring: str) -> None:
|
||||||
self.docstring = docstring
|
self.docstring = docstring
|
||||||
@@ -39,11 +37,11 @@ class PythonDocstring:
|
|||||||
return self.docstring == value.docstring
|
return self.docstring == value.docstring
|
||||||
|
|
||||||
def parse(self) -> docstring_parser.Docstring:
|
def parse(self) -> docstring_parser.Docstring:
|
||||||
'''
|
"""
|
||||||
Parses the docstring.
|
Parses the docstring.
|
||||||
|
|
||||||
:return: The parsed docstring.
|
:return: The parsed docstring.
|
||||||
:rtype: docstring_parser.Docstring
|
:rtype: docstring_parser.Docstring
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return docstring_parser.parse(self.docstring)
|
return docstring_parser.parse(self.docstring)
|
||||||
@@ -13,13 +13,15 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TypeAlias, Union
|
||||||
|
|
||||||
from ...exceptions import (
|
from ...exceptions import (
|
||||||
InvalidNamespaceError,
|
InvalidNamespaceError,
|
||||||
NamespaceNotFoundError,
|
|
||||||
NamespaceExistsError,
|
NamespaceExistsError,
|
||||||
|
NamespaceNotFoundError,
|
||||||
)
|
)
|
||||||
from ...utils import assert_that_is_instance
|
from ...utils import assert_that_is_instance
|
||||||
from typing import Union, TypeAlias, Dict, List
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'PythonNamespace',
|
'PythonNamespace',
|
||||||
@@ -27,13 +29,15 @@ __all__ = [
|
|||||||
'PythonModuleNamespace',
|
'PythonModuleNamespace',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PythonNamespace:
|
class PythonNamespace:
|
||||||
'''
|
"""
|
||||||
The base class for all Python namespaces such as modules or packages.
|
The base class for all Python namespaces such as modules or packages.
|
||||||
|
|
||||||
:param name: The name of the namespace.
|
:param name: The name of the namespace.
|
||||||
:type name: str
|
:type name: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
assert_that_is_instance(name, str)
|
assert_that_is_instance(name, str)
|
||||||
|
|
||||||
@@ -48,7 +52,8 @@ class PythonNamespace:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'{type(self).__name__}({self.name!r})'
|
return f'{type(self).__name__}({self.name!r})'
|
||||||
|
|
||||||
PythonPackageNamespaceDictType: TypeAlias = Dict[
|
|
||||||
|
PythonPackageNamespaceDictType: TypeAlias = dict[
|
||||||
str,
|
str,
|
||||||
Union[
|
Union[
|
||||||
str,
|
str,
|
||||||
@@ -56,20 +61,21 @@ PythonPackageNamespaceDictType: TypeAlias = Dict[
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PythonPackageNamespace(PythonNamespace):
|
class PythonPackageNamespace(PythonNamespace):
|
||||||
'''
|
"""
|
||||||
A subclass of ``PythonNamespace`` for Python package namespaces.
|
A subclass of ``PythonNamespace`` for Python package namespaces.
|
||||||
|
|
||||||
:param name: The name of the namespace.
|
:param name: The name of the namespace.
|
||||||
:type name: str
|
:type name: str
|
||||||
:param sub_namespaces: The sub-namespaces.
|
:param sub_namespaces: The sub-namespaces.
|
||||||
:type sub_namespaces: list[PythonNamespace]
|
:type sub_namespaces: list[PythonNamespace]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
sub_namespaces: List[PythonNamespace],
|
sub_namespaces: list[PythonNamespace],
|
||||||
) -> None:
|
) -> None:
|
||||||
assert_that_is_instance(sub_namespaces, list)
|
assert_that_is_instance(sub_namespaces, list)
|
||||||
|
|
||||||
@@ -77,8 +83,8 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
|
|
||||||
self._sub_namespaces = sub_namespaces
|
self._sub_namespaces = sub_namespaces
|
||||||
|
|
||||||
def namespace(self, sub_namespaces: List[str]) -> PythonNamespace:
|
def namespace(self, sub_namespaces: list[str]) -> PythonNamespace:
|
||||||
'''
|
"""
|
||||||
Returns the namespace object with a specific identifier, such as
|
Returns the namespace object with a specific identifier, such as
|
||||||
``['package', 'subpackage', 'module']``.
|
``['package', 'subpackage', 'module']``.
|
||||||
|
|
||||||
@@ -91,7 +97,7 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
|
|
||||||
:return: The namespace.
|
:return: The namespace.
|
||||||
:rtype: PythonNamespace
|
:rtype: PythonNamespace
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if not sub_namespaces:
|
if not sub_namespaces:
|
||||||
raise InvalidNamespaceError('invalid namespace')
|
raise InvalidNamespaceError('invalid namespace')
|
||||||
@@ -115,7 +121,7 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
self,
|
self,
|
||||||
package: PythonPackageNamespace,
|
package: PythonPackageNamespace,
|
||||||
) -> PythonPackageNamespaceDictType:
|
) -> PythonPackageNamespaceDictType:
|
||||||
'''
|
"""
|
||||||
Returns the names of all sub-namespaces of the specified package
|
Returns the names of all sub-namespaces of the specified package
|
||||||
namespace and keeps the package structure.
|
namespace and keeps the package structure.
|
||||||
|
|
||||||
@@ -124,7 +130,7 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
|
|
||||||
:return: The names if all sub-namespaces.
|
:return: The names if all sub-namespaces.
|
||||||
:rtype: PythonPackageNamespaceDictType
|
:rtype: PythonPackageNamespaceDictType
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sn.name: (
|
sn.name: (
|
||||||
@@ -136,13 +142,13 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def sub_namespace_names(self) -> PythonPackageNamespaceDictType:
|
def sub_namespace_names(self) -> PythonPackageNamespaceDictType:
|
||||||
'''
|
"""
|
||||||
Returns the names of all sub-namespaces and keeps the package
|
Returns the names of all sub-namespaces and keeps the package
|
||||||
structure.
|
structure.
|
||||||
|
|
||||||
:return: The names if all sub-namespaces.
|
:return: The names if all sub-namespaces.
|
||||||
:rtype: PythonPackageNamespaceDictType
|
:rtype: PythonPackageNamespaceDictType
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return self._sub_namespaces_names(self)
|
return self._sub_namespaces_names(self)
|
||||||
|
|
||||||
@@ -159,7 +165,7 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
return type(self).__name__ + repr((self.name, self._sub_namespaces))
|
return type(self).__name__ + repr((self.name, self._sub_namespaces))
|
||||||
|
|
||||||
def __getitem__(self, name: str) -> PythonNamespace:
|
def __getitem__(self, name: str) -> PythonNamespace:
|
||||||
'''
|
"""
|
||||||
Returns a namespace with the specified name.
|
Returns a namespace with the specified name.
|
||||||
|
|
||||||
:param name: The name of the namespace
|
:param name: The name of the namespace
|
||||||
@@ -169,7 +175,7 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
|
|
||||||
:return: The namespace
|
:return: The namespace
|
||||||
:rtype: PythonNamespace
|
:rtype: PythonNamespace
|
||||||
'''
|
"""
|
||||||
|
|
||||||
for sub in self._sub_namespaces:
|
for sub in self._sub_namespaces:
|
||||||
if sub.name == name:
|
if sub.name == name:
|
||||||
@@ -181,7 +187,7 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def add(self, namespace: PythonNamespace) -> None:
|
def add(self, namespace: PythonNamespace) -> None:
|
||||||
'''
|
"""
|
||||||
Adds a namespace.
|
Adds a namespace.
|
||||||
|
|
||||||
:param namespace: The namespace to add.
|
:param namespace: The namespace to add.
|
||||||
@@ -190,7 +196,7 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
:raises NamespaceExistsError: If a namespace with the name of the
|
:raises NamespaceExistsError: If a namespace with the name of the
|
||||||
namespace that is attempted to add
|
namespace that is attempted to add
|
||||||
already exists.
|
already exists.
|
||||||
'''
|
"""
|
||||||
try:
|
try:
|
||||||
self.__getitem__(namespace.name)
|
self.__getitem__(namespace.name)
|
||||||
raise NamespaceExistsError(
|
raise NamespaceExistsError(
|
||||||
@@ -201,16 +207,17 @@ class PythonPackageNamespace(PythonNamespace):
|
|||||||
self._sub_namespaces.append(namespace)
|
self._sub_namespaces.append(namespace)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sub_namespaces(self) -> List[PythonNamespace]:
|
def sub_namespaces(self) -> list[PythonNamespace]:
|
||||||
return self._sub_namespaces
|
return self._sub_namespaces
|
||||||
|
|
||||||
|
|
||||||
class PythonModuleNamespace(PythonNamespace):
|
class PythonModuleNamespace(PythonNamespace):
|
||||||
'''
|
"""
|
||||||
A subclass of ``PythonNamespace`` for Python module namespaces.
|
A subclass of ``PythonNamespace`` for Python module namespaces.
|
||||||
|
|
||||||
:param name: The name of the namespace.
|
:param name: The name of the namespace.
|
||||||
:type name: str
|
:type name: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
class NamespaceError(ValueError):
|
class NamespaceError(ValueError):
|
||||||
'''
|
"""
|
||||||
Base class for namespace errors.
|
Base class for namespace errors.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, *args: object, namespace_identifier: str = '') -> None:
|
def __init__(self, *args: object, namespace_identifier: str = '') -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
self.namespace_identifier = namespace_identifier
|
self.namespace_identifier = namespace_identifier
|
||||||
@@ -25,23 +27,24 @@ class NamespaceError(ValueError):
|
|||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
return f'{self.args[0]}{
|
return f'{self.args[0]}{
|
||||||
": "
|
": " if self.namespace_identifier and self.args[0] else ""
|
||||||
if self.namespace_identifier and self.args[0]
|
}{self.namespace_identifier if self.args[0] else ""}'
|
||||||
else ""
|
|
||||||
}{
|
|
||||||
self.namespace_identifier
|
|
||||||
if self.args[0]
|
|
||||||
else ""
|
|
||||||
}'
|
|
||||||
|
|
||||||
class InvalidNamespaceError(NamespaceError): ...
|
class InvalidNamespaceError(NamespaceError): ...
|
||||||
|
|
||||||
|
|
||||||
class NamespaceNotFoundError(NamespaceError): ...
|
class NamespaceNotFoundError(NamespaceError): ...
|
||||||
|
|
||||||
|
|
||||||
class NamespaceExistsError(NamespaceError): ...
|
class NamespaceExistsError(NamespaceError): ...
|
||||||
|
|
||||||
|
|
||||||
class PythonIdentifierError(ValueError):
|
class PythonIdentifierError(ValueError):
|
||||||
'''
|
"""
|
||||||
Base class for Python identifier errors.
|
Base class for Python identifier errors.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, *args: object, identifier: str = '') -> None:
|
def __init__(self, *args: object, identifier: str = '') -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
self.identifier = identifier
|
self.identifier = identifier
|
||||||
@@ -51,21 +54,18 @@ class PythonIdentifierError(ValueError):
|
|||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
return f'{self.args[0]}{
|
return f'{self.args[0]}{
|
||||||
": "
|
": " if self.identifier and self.args[0] else ""
|
||||||
if self.identifier and self.args[0]
|
}{self.identifier if self.args[0] else ""}'
|
||||||
else ""
|
|
||||||
}{
|
|
||||||
self.identifier
|
|
||||||
if self.args[0]
|
|
||||||
else ""
|
|
||||||
}'
|
|
||||||
|
|
||||||
class InvalidPythonIdentifierError(PythonIdentifierError): ...
|
class InvalidPythonIdentifierError(PythonIdentifierError): ...
|
||||||
|
|
||||||
|
|
||||||
class PythonAnnotationError(ValueError):
|
class PythonAnnotationError(ValueError):
|
||||||
'''
|
"""
|
||||||
Base class for Python annotation errors.
|
Base class for Python annotation errors.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, *args: object, annotation: str = '') -> None:
|
def __init__(self, *args: object, annotation: str = '') -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
self.annotation = annotation
|
self.annotation = annotation
|
||||||
@@ -75,21 +75,18 @@ class PythonAnnotationError(ValueError):
|
|||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
return f'{self.args[0]}{
|
return f'{self.args[0]}{
|
||||||
": "
|
": " if self.annotation and self.args[0] else ""
|
||||||
if self.annotation and self.args[0]
|
}{self.annotation if self.args[0] else ""}'
|
||||||
else ""
|
|
||||||
}{
|
|
||||||
self.annotation
|
|
||||||
if self.args[0]
|
|
||||||
else ""
|
|
||||||
}'
|
|
||||||
|
|
||||||
class InvalidPythonAnnotationError(PythonAnnotationError): ...
|
class InvalidPythonAnnotationError(PythonAnnotationError): ...
|
||||||
|
|
||||||
|
|
||||||
class PythonDefinitionError(ValueError):
|
class PythonDefinitionError(ValueError):
|
||||||
'''
|
"""
|
||||||
Base class for Python definition errors.
|
Base class for Python definition errors.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, *args: object, definition: str = '') -> None:
|
def __init__(self, *args: object, definition: str = '') -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
self.definition = definition
|
self.definition = definition
|
||||||
@@ -99,13 +96,8 @@ class PythonDefinitionError(ValueError):
|
|||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
return f'{self.args[0]}{
|
return f'{self.args[0]}{
|
||||||
": "
|
": " if self.definition and self.args[0] else ""
|
||||||
if self.definition and self.args[0]
|
}{self.definition if self.args[0] else ""}'
|
||||||
else ""
|
|
||||||
}{
|
|
||||||
self.definition
|
|
||||||
if self.args[0]
|
|
||||||
else ""
|
|
||||||
}'
|
|
||||||
|
|
||||||
class PythonArgumentStructureError(PythonDefinitionError): ...
|
class PythonArgumentStructureError(PythonDefinitionError): ...
|
||||||
+54
-39
@@ -12,30 +12,32 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
The utilities for jcloud_docsgen
|
The utilities for jcloud_docsgen
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import pathlib
|
|
||||||
import os
|
|
||||||
import errno
|
import errno
|
||||||
from typing import Union
|
import os
|
||||||
|
import pathlib
|
||||||
import types
|
import types
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from warnings import deprecated
|
from warnings import deprecated
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from typing_extensions import deprecated
|
from typing_extensions import deprecated
|
||||||
|
|
||||||
|
|
||||||
@deprecated('Use pathlib.Path instead.')
|
@deprecated('Use pathlib.Path instead.')
|
||||||
class ExistingDirectory(pathlib.Path):
|
class ExistingDirectory(pathlib.Path):
|
||||||
'''
|
"""
|
||||||
pathlib.Path subclass that is a directory.
|
pathlib.Path subclass that is a directory.
|
||||||
|
|
||||||
ExistingDirectory represents a filesystem path but it also checks
|
ExistingDirectory represents a filesystem path but it also checks
|
||||||
whether the path exists and is not a file but a directory. It
|
whether the path exists and is not a file but a directory. It
|
||||||
inherits from pathlib.Path.
|
inherits from pathlib.Path.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, path, *args, **kwargs) -> None:
|
def __init__(self, path, *args, **kwargs) -> None:
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
@@ -47,15 +49,17 @@ class ExistingDirectory(pathlib.Path):
|
|||||||
raise NotADirectoryError(errno.ENOTDIR, 'not a directory', path)
|
raise NotADirectoryError(errno.ENOTDIR, 'not a directory', path)
|
||||||
super().__init__(*(path, *args), **kwargs)
|
super().__init__(*(path, *args), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@deprecated('Use pathlib.Path instead.')
|
@deprecated('Use pathlib.Path instead.')
|
||||||
class ExistingFile(pathlib.Path):
|
class ExistingFile(pathlib.Path):
|
||||||
'''
|
"""
|
||||||
pathlib.Path subclass that is a file.
|
pathlib.Path subclass that is a file.
|
||||||
|
|
||||||
ExistingFile represents a filesystem path but it also checks whether
|
ExistingFile represents a filesystem path but it also checks whether
|
||||||
the path exists and is not a directory but a file. It inherits from
|
the path exists and is not a directory but a file. It inherits from
|
||||||
pathlib.Path.
|
pathlib.Path.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, path, *args, **kwargs) -> None:
|
def __init__(self, path, *args, **kwargs) -> None:
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
@@ -67,8 +71,9 @@ class ExistingFile(pathlib.Path):
|
|||||||
raise IsADirectoryError(errno.EISDIR, 'is a directory', path)
|
raise IsADirectoryError(errno.EISDIR, 'is a directory', path)
|
||||||
super().__init__(*(path, *args), **kwargs)
|
super().__init__(*(path, *args), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def _quote(string: str, quotation_mark: str) -> str:
|
def _quote(string: str, quotation_mark: str) -> str:
|
||||||
'''
|
"""
|
||||||
Quotes a string.
|
Quotes a string.
|
||||||
|
|
||||||
:param string: The string to quote
|
:param string: The string to quote
|
||||||
@@ -78,16 +83,17 @@ def _quote(string: str, quotation_mark: str) -> str:
|
|||||||
|
|
||||||
:return: The quoted string
|
:return: The quoted string
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return quotation_mark + string + quotation_mark
|
return quotation_mark + string + quotation_mark
|
||||||
|
|
||||||
|
|
||||||
def human_readable_list(
|
def human_readable_list(
|
||||||
ls: list,
|
ls: list,
|
||||||
final_separator: str = 'and',
|
final_separator: str = 'and',
|
||||||
quotation_mark: str = '',
|
quotation_mark: str = '',
|
||||||
) -> str:
|
) -> str:
|
||||||
'''
|
"""
|
||||||
Generates a human-readable list from a list.
|
Generates a human-readable list from a list.
|
||||||
|
|
||||||
:param ls: The list.
|
:param ls: The list.
|
||||||
@@ -101,7 +107,7 @@ def human_readable_list(
|
|||||||
|
|
||||||
:return: The human-readable list.
|
:return: The human-readable list.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# empty list
|
# empty list
|
||||||
if not ls:
|
if not ls:
|
||||||
@@ -111,18 +117,26 @@ def human_readable_list(
|
|||||||
if len(ls) == 1:
|
if len(ls) == 1:
|
||||||
return _quote(str(ls[0]), quotation_mark)
|
return _quote(str(ls[0]), quotation_mark)
|
||||||
|
|
||||||
return ', '.join(
|
return (
|
||||||
|
', '.join(
|
||||||
[
|
[
|
||||||
_quote(
|
_quote(
|
||||||
str(obj),
|
str(obj), quotation_mark
|
||||||
quotation_mark
|
|
||||||
) # quote the string representation of the object
|
) # quote the string representation of the object
|
||||||
for obj in ls[:-1] # do not include the last element of the list
|
for obj in ls[
|
||||||
|
:-1
|
||||||
|
] # do not include the last element of the list
|
||||||
],
|
],
|
||||||
) + ' ' + final_separator + ' ' + _quote(str(ls[-1]), quotation_mark)
|
)
|
||||||
|
+ ' '
|
||||||
|
+ final_separator
|
||||||
|
+ ' '
|
||||||
|
+ _quote(str(ls[-1]), quotation_mark)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _list_type_names(types: list[type]) -> list[str]:
|
def _list_type_names(types: list[type]) -> list[str]:
|
||||||
'''
|
"""
|
||||||
Converts a list of types into a list of their names (``__name__``
|
Converts a list of types into a list of their names (``__name__``
|
||||||
attribute)
|
attribute)
|
||||||
|
|
||||||
@@ -131,22 +145,19 @@ def _list_type_names(types: list[type]) -> list[str]:
|
|||||||
|
|
||||||
:return: The list of the names of the types
|
:return: The list of the names of the types
|
||||||
:rtype: list[str]
|
:rtype: list[str]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return [tp.__name__ for tp in types]
|
return [tp.__name__ for tp in types]
|
||||||
|
|
||||||
|
|
||||||
def assert_that_is_instance(
|
def assert_that_is_instance(
|
||||||
obj: object,
|
obj: object,
|
||||||
class_or_tuple: Union[
|
class_or_tuple: type | types.UnionType | tuple[type, ...],
|
||||||
type,
|
|
||||||
types.UnionType,
|
|
||||||
tuple[type, ...],
|
|
||||||
],
|
|
||||||
) -> None:
|
) -> None:
|
||||||
if not isinstance(class_or_tuple, Union[type, types.UnionType, tuple]):
|
if not isinstance(class_or_tuple, Union[type, types.UnionType, tuple]):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f'class_or_tuple: expected \'Union[type, types.UnionType, '
|
f"class_or_tuple: expected 'Union[type, types.UnionType, "
|
||||||
f'tuple[type, ...]]\', \'got {type(class_or_tuple).__name__}\'',
|
f"tuple[type, ...]]', 'got {type(class_or_tuple).__name__}'",
|
||||||
)
|
)
|
||||||
if not isinstance(obj, class_or_tuple):
|
if not isinstance(obj, class_or_tuple):
|
||||||
if isinstance(class_or_tuple, (tuple, types.UnionType)):
|
if isinstance(class_or_tuple, (tuple, types.UnionType)):
|
||||||
@@ -157,18 +168,21 @@ def assert_that_is_instance(
|
|||||||
else:
|
else:
|
||||||
exception_message_expected = ''
|
exception_message_expected = ''
|
||||||
exception_message_expected += human_readable_list(
|
exception_message_expected += human_readable_list(
|
||||||
_list_type_names(class_or_tuple), 'or', '\'',
|
_list_type_names(class_or_tuple),
|
||||||
|
'or',
|
||||||
|
"'",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
exception_message_expected = '\'' + class_or_tuple.__name__ + '\''
|
exception_message_expected = "'" + class_or_tuple.__name__ + "'"
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f'expected {exception_message_expected}, got \'{type(
|
f"expected {exception_message_expected}, got '{
|
||||||
obj
|
type(obj).__name__
|
||||||
).__name__}\'',
|
}'",
|
||||||
)
|
)
|
||||||
|
|
||||||
def non_empty_str(value: Union[str, None]) -> Union[str, None]:
|
|
||||||
'''
|
def non_empty_str(value: str | None) -> str | None:
|
||||||
|
"""
|
||||||
Returns the value or ``None``.
|
Returns the value or ``None``.
|
||||||
|
|
||||||
Returns the value if it is not empty and not `Ǹone``; otherwise,
|
Returns the value if it is not empty and not `Ǹone``; otherwise,
|
||||||
@@ -177,11 +191,11 @@ def non_empty_str(value: Union[str, None]) -> Union[str, None]:
|
|||||||
of spaces, it is also considered empty.
|
of spaces, it is also considered empty.
|
||||||
|
|
||||||
:param value: The value.
|
:param value: The value.
|
||||||
:type value: Union[str, None]
|
:type value: str | None
|
||||||
|
|
||||||
:return: The value or ``None``.
|
:return: The value or ``None``.
|
||||||
:rtype: Union[str, None]
|
:rtype: str | None
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
@@ -191,11 +205,12 @@ def non_empty_str(value: Union[str, None]) -> Union[str, None]:
|
|||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def get_relative_path_segments(
|
def get_relative_path_segments(
|
||||||
path: pathlib.Path,
|
path: pathlib.Path,
|
||||||
root: pathlib.Path,
|
root: pathlib.Path,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
'''
|
"""
|
||||||
Returns the path segments relative to ``root``.
|
Returns the path segments relative to ``root``.
|
||||||
|
|
||||||
:param path: The path.
|
:param path: The path.
|
||||||
@@ -205,7 +220,7 @@ def get_relative_path_segments(
|
|||||||
|
|
||||||
:return: The path segments relative to ``root``.
|
:return: The path segments relative to ``root``.
|
||||||
:rtype: list[str]
|
:rtype: list[str]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return list(
|
return list(
|
||||||
path.resolve()
|
path.resolve()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def test_cli():
|
def test_cli():
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
[
|
[
|
||||||
@@ -27,8 +28,9 @@ def test_cli():
|
|||||||
text=True,
|
text=True,
|
||||||
)
|
)
|
||||||
stdout = result.stdout.lower()
|
stdout = result.stdout.lower()
|
||||||
assert \
|
assert (
|
||||||
'usage:' in stdout and \
|
'usage:' in stdout
|
||||||
'positional arguments' in stdout and \
|
and 'positional arguments' in stdout
|
||||||
'options' in stdout and \
|
and 'options' in stdout
|
||||||
'--help' in stdout
|
and '--help' in stdout
|
||||||
|
)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
def f(a: int = 42) -> str:
|
def f(a: int = 42) -> str:
|
||||||
'''
|
"""
|
||||||
Does something.
|
Does something.
|
||||||
|
|
||||||
:param a: A parameter.
|
:param a: A parameter.
|
||||||
@@ -7,5 +7,5 @@ def f(a: int = 42) -> str:
|
|||||||
|
|
||||||
:return: A string.
|
:return: A string.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
...
|
...
|
||||||
@@ -13,22 +13,31 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from jcloud_docsgen.cli.cli import create_argument_parser
|
from jcloud_docsgen.cli.cli import create_argument_parser
|
||||||
|
|
||||||
@pytest.mark.parametrize('lang', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'lang',
|
||||||
|
[
|
||||||
'javascript',
|
'javascript',
|
||||||
'Python',
|
'Python',
|
||||||
'PYTHON',
|
'PYTHON',
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_language_error(lang):
|
def test_language_error(lang):
|
||||||
parser = create_argument_parser()
|
parser = create_argument_parser()
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
parser.parse_args([lang])
|
parser.parse_args([lang])
|
||||||
|
|
||||||
@pytest.mark.parametrize('lang', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'lang',
|
||||||
|
[
|
||||||
'python',
|
'python',
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_language_success(lang):
|
def test_language_success(lang):
|
||||||
parser = create_argument_parser()
|
parser = create_argument_parser()
|
||||||
parser.parse_args([lang])
|
parser.parse_args([lang])
|
||||||
@@ -13,14 +13,20 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from jcloud_docsgen.cli.cli import create_argument_parser, main as cli_main
|
|
||||||
|
from jcloud_docsgen.cli.cli import create_argument_parser
|
||||||
|
from jcloud_docsgen.cli.cli import main as cli_main
|
||||||
from jcloud_docsgen.cli.languages.python import parse_sections_list
|
from jcloud_docsgen.cli.languages.python import parse_sections_list
|
||||||
from jcloud_docsgen.core.python import (
|
from jcloud_docsgen.core.python import (
|
||||||
PythonDefinitionDocumentationIncludeSections
|
PythonDefinitionDocumentationIncludeSections,
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize('include,exclude,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'include,exclude,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
@@ -115,49 +121,62 @@ from jcloud_docsgen.core.python import (
|
|||||||
decorators=True,
|
decorators=True,
|
||||||
class_body=True,
|
class_body=True,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
])
|
|
||||||
def test_parse_sections_list(include, exclude, expected):
|
def test_parse_sections_list(include, exclude, expected):
|
||||||
assert parse_sections_list(include, exclude) == expected
|
assert parse_sections_list(include, exclude) == expected
|
||||||
|
|
||||||
|
|
||||||
def test_sections_error():
|
def test_sections_error():
|
||||||
parser = create_argument_parser()
|
parser = create_argument_parser()
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
parser.parse_args([
|
parser.parse_args(
|
||||||
|
[
|
||||||
'python',
|
'python',
|
||||||
'-s',
|
'-s',
|
||||||
'param', # correct: `params`
|
'param', # correct: `params`
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
parser.parse_args([
|
parser.parse_args(
|
||||||
|
[
|
||||||
'python',
|
'python',
|
||||||
'-s',
|
'-s',
|
||||||
'params',
|
'params',
|
||||||
'docstrng', # correct: `docstring`
|
'docstrng', # correct: `docstring`
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_sections_success():
|
def test_sections_success():
|
||||||
parser = create_argument_parser()
|
parser = create_argument_parser()
|
||||||
|
|
||||||
parser.parse_args([
|
parser.parse_args(
|
||||||
|
[
|
||||||
'python',
|
'python',
|
||||||
'-s',
|
'-s',
|
||||||
'params',
|
'params',
|
||||||
])
|
]
|
||||||
parser.parse_args([
|
)
|
||||||
|
parser.parse_args(
|
||||||
|
[
|
||||||
'python',
|
'python',
|
||||||
'-s',
|
'-s',
|
||||||
'params',
|
'params',
|
||||||
'docstring',
|
'docstring',
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_docs_generation(expectdir):
|
def test_docs_generation(expectdir):
|
||||||
with expectdir('python_project') as output_dir:
|
with expectdir('python_project') as output_dir:
|
||||||
output_dir: pathlib.Path
|
output_dir: pathlib.Path
|
||||||
input_dir = pathlib.Path(__file__).parent / 'python_project/project'
|
input_dir = pathlib.Path(__file__).parent / 'python_project/project'
|
||||||
cli_main([
|
cli_main(
|
||||||
|
[
|
||||||
'python',
|
'python',
|
||||||
'-s',
|
'-s',
|
||||||
'params',
|
'params',
|
||||||
@@ -165,4 +184,5 @@ def test_docs_generation(expectdir):
|
|||||||
'-HTo',
|
'-HTo',
|
||||||
str(output_dir),
|
str(output_dir),
|
||||||
str(input_dir),
|
str(input_dir),
|
||||||
])
|
]
|
||||||
|
)
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -12,15 +12,20 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python import PythonDocumentationGenerator
|
from src.jcloud_docsgen.core.python import PythonDocumentationGenerator
|
||||||
from src.jcloud_docsgen.core.python.namespaces import (
|
from src.jcloud_docsgen.core.python.namespaces import (
|
||||||
PythonModuleNamespace,
|
PythonModuleNamespace,
|
||||||
PythonPackageNamespace,
|
PythonPackageNamespace,
|
||||||
)
|
)
|
||||||
import pytest
|
|
||||||
import pathlib
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('python_documentation_generator,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'python_documentation_generator,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonDocumentationGenerator(
|
PythonDocumentationGenerator(
|
||||||
pathlib.Path(
|
pathlib.Path(
|
||||||
@@ -44,9 +49,7 @@ import pathlib
|
|||||||
[
|
[
|
||||||
PythonPackageNamespace(
|
PythonPackageNamespace(
|
||||||
'pkg',
|
'pkg',
|
||||||
[
|
[PythonModuleNamespace('module')],
|
||||||
PythonModuleNamespace('module')
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -67,23 +70,25 @@ import pathlib
|
|||||||
PythonPackageNamespace(
|
PythonPackageNamespace(
|
||||||
'pkg',
|
'pkg',
|
||||||
[
|
[
|
||||||
PythonModuleNamespace(
|
PythonModuleNamespace('module2'),
|
||||||
'module2'
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonDocumentationGenerator_collect_modules(
|
def test_PythonDocumentationGenerator_collect_modules(
|
||||||
python_documentation_generator: PythonDocumentationGenerator,
|
python_documentation_generator: PythonDocumentationGenerator,
|
||||||
expected,
|
expected,
|
||||||
):
|
):
|
||||||
assert python_documentation_generator.namespace() == expected
|
assert python_documentation_generator.namespace() == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('dir', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'dir',
|
||||||
|
[
|
||||||
d.name
|
d.name
|
||||||
for d in pathlib.Path(
|
for d in pathlib.Path(
|
||||||
(
|
(
|
||||||
@@ -91,7 +96,8 @@ def test_PythonDocumentationGenerator_collect_modules(
|
|||||||
'ntationGenerator/test_generate_documentation/'
|
'ntationGenerator/test_generate_documentation/'
|
||||||
),
|
),
|
||||||
).iterdir()
|
).iterdir()
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_generate_documentation(dir, expectdir):
|
def test_generate_documentation(dir, expectdir):
|
||||||
with expectdir(
|
with expectdir(
|
||||||
'test_PythonDocumentationGenerator/test_generate_documentation/' + dir
|
'test_PythonDocumentationGenerator/test_generate_documentation/' + dir
|
||||||
@@ -104,9 +110,9 @@ def test_generate_documentation(dir, expectdir):
|
|||||||
'tests/unit/core/python/_core/test_PythonDocume'
|
'tests/unit/core/python/_core/test_PythonDocume'
|
||||||
'ntationGenerator/test_generate_documentation/'
|
'ntationGenerator/test_generate_documentation/'
|
||||||
),
|
),
|
||||||
) /
|
)
|
||||||
dir /
|
/ dir
|
||||||
'project',
|
/ 'project',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pathlib.Path(str(d)),
|
pathlib.Path(str(d)),
|
||||||
|
|||||||
+2
@@ -1,8 +1,10 @@
|
|||||||
class UserNotFoundError(Exception):
|
class UserNotFoundError(Exception):
|
||||||
"""User does not exist."""
|
"""User does not exist."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InvalidEmailError(Exception):
|
class InvalidEmailError(Exception):
|
||||||
"""Email address is invalid."""
|
"""Email address is invalid."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
+1
@@ -7,6 +7,7 @@ class User:
|
|||||||
"""
|
"""
|
||||||
Represents a user.
|
Represents a user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
email: str
|
email: str
|
||||||
|
|||||||
+3
-10
@@ -13,23 +13,16 @@ class UserService:
|
|||||||
self.storage = storage
|
self.storage = storage
|
||||||
|
|
||||||
def create_user(
|
def create_user(
|
||||||
self,
|
self, user_id: str, name: str, email: str, age: int | None = None
|
||||||
user_id: str,
|
|
||||||
name: str,
|
|
||||||
email: str,
|
|
||||||
age: int | None = None
|
|
||||||
) -> User:
|
) -> User:
|
||||||
"""
|
"""
|
||||||
Creates and stores a new user.
|
Creates and stores a new user.
|
||||||
"""
|
"""
|
||||||
if not is_valid_email(email):
|
if not is_valid_email(email):
|
||||||
raise InvalidEmailError(f"Invalid email: {email}")
|
raise InvalidEmailError(f'Invalid email: {email}')
|
||||||
|
|
||||||
user = User(
|
user = User(
|
||||||
id=user_id,
|
id=user_id, name=normalize_name(name), email=email, age=age
|
||||||
name=normalize_name(name),
|
|
||||||
email=email,
|
|
||||||
age=age
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.storage.add(user)
|
self.storage.add(user)
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@ def is_valid_email(email: str) -> bool:
|
|||||||
"""
|
"""
|
||||||
Checks whether an email address is valid.
|
Checks whether an email address is valid.
|
||||||
"""
|
"""
|
||||||
pattern = r"^[\w\.-]+@[\w\.-]+\.\w+$"
|
pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
|
||||||
return re.match(pattern, email) is not None
|
return re.match(pattern, email) is not None
|
||||||
|
|
||||||
|
|
||||||
@@ -13,4 +13,4 @@ def normalize_name(name: str) -> str:
|
|||||||
"""
|
"""
|
||||||
Removes extra spaces and formats the name.
|
Removes extra spaces and formats the name.
|
||||||
"""
|
"""
|
||||||
return " ".join(name.strip().split()).title()
|
return ' '.join(name.strip().split()).title()
|
||||||
@@ -12,10 +12,13 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python import PythonModuleDocumentationGenerator
|
from src.jcloud_docsgen.core.python import PythonModuleDocumentationGenerator
|
||||||
from src.jcloud_docsgen.exceptions import InvalidNamespaceError
|
from src.jcloud_docsgen.exceptions import InvalidNamespaceError
|
||||||
import pathlib
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'module_path,src_directory,expected_exception,expected_exception_msg',
|
'module_path,src_directory,expected_exception,expected_exception_msg',
|
||||||
@@ -216,7 +219,10 @@ def test_PythonModuleDocumentationGenerator_exceptions(
|
|||||||
PythonModuleDocumentationGenerator(module_path, src_directory)
|
PythonModuleDocumentationGenerator(module_path, src_directory)
|
||||||
assert str(exc_info.value) == expected_exception_msg
|
assert str(exc_info.value) == expected_exception_msg
|
||||||
|
|
||||||
@pytest.mark.parametrize('module_documentation_generator,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'module_documentation_generator,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonModuleDocumentationGenerator(
|
PythonModuleDocumentationGenerator(
|
||||||
pathlib.Path(
|
pathlib.Path(
|
||||||
@@ -231,7 +237,7 @@ def test_PythonModuleDocumentationGenerator_exceptions(
|
|||||||
allow_tables=True,
|
allow_tables=True,
|
||||||
allow_html=False,
|
allow_html=False,
|
||||||
),
|
),
|
||||||
'''# Module `pkg.module`
|
"""# Module `pkg.module`
|
||||||
|
|
||||||
A test module.
|
A test module.
|
||||||
|
|
||||||
@@ -398,9 +404,8 @@ Barks.
|
|||||||
|
|
||||||
### Docstring
|
### Docstring
|
||||||
|
|
||||||
A dog.'''
|
A dog.""",
|
||||||
),
|
),
|
||||||
|
|
||||||
(
|
(
|
||||||
PythonModuleDocumentationGenerator(
|
PythonModuleDocumentationGenerator(
|
||||||
pathlib.Path(
|
pathlib.Path(
|
||||||
@@ -413,7 +418,7 @@ A dog.'''
|
|||||||
allow_tables=True,
|
allow_tables=True,
|
||||||
allow_html=False,
|
allow_html=False,
|
||||||
),
|
),
|
||||||
'''# Module `module`
|
"""# Module `module`
|
||||||
|
|
||||||
A test module.
|
A test module.
|
||||||
|
|
||||||
@@ -580,9 +585,8 @@ Barks.
|
|||||||
|
|
||||||
### Docstring
|
### Docstring
|
||||||
|
|
||||||
A dog.'''
|
A dog.""",
|
||||||
),
|
),
|
||||||
|
|
||||||
(
|
(
|
||||||
PythonModuleDocumentationGenerator(
|
PythonModuleDocumentationGenerator(
|
||||||
pathlib.Path(
|
pathlib.Path(
|
||||||
@@ -596,7 +600,7 @@ A dog.'''
|
|||||||
allow_html=True,
|
allow_html=True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'''# Module `module`
|
"""# Module `module`
|
||||||
|
|
||||||
A test module.
|
A test module.
|
||||||
|
|
||||||
@@ -630,8 +634,8 @@ Return type: `int | float`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds '''
|
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds """
|
||||||
'''two numbers.
|
"""two numbers.
|
||||||
|
|
||||||
:param a: The first number.
|
:param a: The first number.
|
||||||
:type a: int | float
|
:type a: int | float
|
||||||
@@ -698,8 +702,8 @@ Return type: `str`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h5 style="display:inline">Docstring</h5></summary>'''
|
<details><summary><h5 style="display:inline">Docstring</h5></summary>"""
|
||||||
'''Returns a greeting.
|
"""Returns a greeting.
|
||||||
|
|
||||||
:return: The greeting.
|
:return: The greeting.
|
||||||
:rtype: str</details>
|
:rtype: str</details>
|
||||||
@@ -708,8 +712,8 @@ Return type: `str`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h3 style="display:inline">Docstring</h3></summary>A '''
|
<details><summary><h3 style="display:inline">Docstring</h3></summary>A """
|
||||||
'''class representing an animal.
|
"""class representing an animal.
|
||||||
|
|
||||||
:param name: Its name.
|
:param name: Its name.
|
||||||
:type name: str</details>
|
:type name: str</details>
|
||||||
@@ -750,18 +754,17 @@ Return type: `None`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.'''
|
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks."""
|
||||||
'''</details>
|
"""</details>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h3 style="display:inline">Docstring</h3></summary>A dog.'''
|
<details><summary><h3 style="display:inline">Docstring</h3></summary>A dog."""
|
||||||
'</details>'
|
'</details>'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
(
|
(
|
||||||
PythonModuleDocumentationGenerator(
|
PythonModuleDocumentationGenerator(
|
||||||
pathlib.Path(
|
pathlib.Path(
|
||||||
@@ -775,7 +778,7 @@ Return type: `None`
|
|||||||
allow_html=True,
|
allow_html=True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'''# Module `module`
|
"""# Module `module`
|
||||||
|
|
||||||
A test module.
|
A test module.
|
||||||
|
|
||||||
@@ -824,8 +827,8 @@ Return type: `int | float`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds '''
|
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds """
|
||||||
'''two numbers.
|
"""two numbers.
|
||||||
|
|
||||||
:param a: The first number.
|
:param a: The first number.
|
||||||
:type a: int | float
|
:type a: int | float
|
||||||
@@ -913,8 +916,8 @@ Return type: `str`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h5 style="display:inline">Docstring</h5></summary>'''
|
<details><summary><h5 style="display:inline">Docstring</h5></summary>"""
|
||||||
'''Returns a greeting.
|
"""Returns a greeting.
|
||||||
|
|
||||||
:return: The greeting.
|
:return: The greeting.
|
||||||
:rtype: str</details>
|
:rtype: str</details>
|
||||||
@@ -923,8 +926,8 @@ Return type: `str`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h3 style="display:inline">Docstring</h3></summary>A '''
|
<details><summary><h3 style="display:inline">Docstring</h3></summary>A """
|
||||||
'''class representing an animal.
|
"""class representing an animal.
|
||||||
|
|
||||||
:param name: Its name.
|
:param name: Its name.
|
||||||
:type name: str</details>
|
:type name: str</details>
|
||||||
@@ -971,18 +974,17 @@ Return type: `None`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.'''
|
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks."""
|
||||||
'''</details>
|
"""</details>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary><h3 style="display:inline">Docstring</h3></summary>A '''
|
<details><summary><h3 style="display:inline">Docstring</h3></summary>A """
|
||||||
'dog.</details>'
|
'dog.</details>'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
(
|
(
|
||||||
PythonModuleDocumentationGenerator(
|
PythonModuleDocumentationGenerator(
|
||||||
pathlib.Path(
|
pathlib.Path(
|
||||||
@@ -995,7 +997,7 @@ Return type: `None`
|
|||||||
allow_tables=False,
|
allow_tables=False,
|
||||||
allow_html=False,
|
allow_html=False,
|
||||||
),
|
),
|
||||||
'''# Module `module`
|
"""# Module `module`
|
||||||
|
|
||||||
A test module.
|
A test module.
|
||||||
|
|
||||||
@@ -1204,9 +1206,10 @@ Barks.
|
|||||||
|
|
||||||
### Docstring
|
### Docstring
|
||||||
|
|
||||||
A dog.'''
|
A dog.""",
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonModuleDocumentationGenerator_exceptions(
|
def test_PythonModuleDocumentationGenerator_exceptions(
|
||||||
module_documentation_generator,
|
module_documentation_generator,
|
||||||
expected,
|
expected,
|
||||||
|
|||||||
@@ -12,16 +12,21 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python._core import _argument_kind_human_readable
|
|
||||||
from src.jcloud_docsgen.core.python.arguments import PythonArgumentKind
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.parametrize('argument_kind,expected', [
|
from src.jcloud_docsgen.core.python._core import _argument_kind_human_readable
|
||||||
|
from src.jcloud_docsgen.core.python.arguments import PythonArgumentKind
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'argument_kind,expected',
|
||||||
|
[
|
||||||
(PythonArgumentKind.NORMAL, 'normal'),
|
(PythonArgumentKind.NORMAL, 'normal'),
|
||||||
(PythonArgumentKind.POSITIONAL_ONLY, 'positional-only'),
|
(PythonArgumentKind.POSITIONAL_ONLY, 'positional-only'),
|
||||||
(PythonArgumentKind.KEYWORD_ONLY, 'keyword-only'),
|
(PythonArgumentKind.KEYWORD_ONLY, 'keyword-only'),
|
||||||
(PythonArgumentKind.VARARG, 'vararg'),
|
(PythonArgumentKind.VARARG, 'vararg'),
|
||||||
(PythonArgumentKind.KWARGS, 'kwargs'),
|
(PythonArgumentKind.KWARGS, 'kwargs'),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test__argument_kind_human_readable(argument_kind, expected):
|
def test__argument_kind_human_readable(argument_kind, expected):
|
||||||
assert _argument_kind_human_readable(argument_kind) == expected
|
assert _argument_kind_human_readable(argument_kind) == expected
|
||||||
@@ -12,21 +12,24 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import ast
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python._core import _collect_definitions
|
from src.jcloud_docsgen.core.python._core import _collect_definitions
|
||||||
|
from src.jcloud_docsgen.core.python.arguments import (
|
||||||
|
PythonArgumentKind,
|
||||||
|
PythonFunctionArgument,
|
||||||
|
)
|
||||||
from src.jcloud_docsgen.core.python.definitions import (
|
from src.jcloud_docsgen.core.python.definitions import (
|
||||||
PythonClassDefinition,
|
PythonClassDefinition,
|
||||||
PythonFunctionDefinition,
|
PythonFunctionDefinition,
|
||||||
)
|
)
|
||||||
from src.jcloud_docsgen.core.python.arguments import (
|
|
||||||
PythonFunctionArgument,
|
|
||||||
PythonArgumentKind,
|
|
||||||
)
|
|
||||||
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
||||||
import pytest
|
|
||||||
import ast
|
|
||||||
|
|
||||||
def module(source_code: str) -> ast.Module:
|
def module(source_code: str) -> ast.Module:
|
||||||
'''
|
"""
|
||||||
Returns an AST module of the source code.
|
Returns an AST module of the source code.
|
||||||
|
|
||||||
:param source_code: The module source code.
|
:param source_code: The module source code.
|
||||||
@@ -34,11 +37,14 @@ def module(source_code: str) -> ast.Module:
|
|||||||
|
|
||||||
:return: The AST module.
|
:return: The AST module.
|
||||||
:rtype: ast.Module
|
:rtype: ast.Module
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return ast.parse(source_code)
|
return ast.parse(source_code)
|
||||||
|
|
||||||
@pytest.mark.parametrize('module,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'module,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
module('def add(a, b):\n\treturn a + b'),
|
module('def add(a, b):\n\treturn a + b'),
|
||||||
[
|
[
|
||||||
@@ -169,7 +175,7 @@ class Something:
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
module('''from src.jcloud_docsgen.core.python import (
|
module("""from src.jcloud_docsgen.core.python import (
|
||||||
PythonDocumentationGenerator
|
PythonDocumentationGenerator
|
||||||
)
|
)
|
||||||
from src.jcloud_docsgen.core.python.namespaces import (
|
from src.jcloud_docsgen.core.python.namespaces import (
|
||||||
@@ -235,7 +241,7 @@ def test_PythonDocumentationGenerator_collect_modules(
|
|||||||
python_documentation_generator: PythonDocumentationGenerator,
|
python_documentation_generator: PythonDocumentationGenerator,
|
||||||
expected
|
expected
|
||||||
):
|
):
|
||||||
assert python_documentation_generator.namespace() == expected'''),
|
assert python_documentation_generator.namespace() == expected"""),
|
||||||
[
|
[
|
||||||
PythonFunctionDefinition(
|
PythonFunctionDefinition(
|
||||||
'test_PythonDocumentationGenerator_type_exceptions',
|
'test_PythonDocumentationGenerator_type_exceptions',
|
||||||
@@ -251,18 +257,18 @@ def test_PythonDocumentationGenerator_collect_modules(
|
|||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
'''pytest.mark.parametrize('project_directo'''
|
"""pytest.mark.parametrize('project_directo"""
|
||||||
'''ry,docs_directory', [(1, 1), (1, None), '''
|
"""ry,docs_directory', [(1, 1), (1, None), """
|
||||||
'''(42, None), (pathlib.Path('tests'), 1), '''
|
"""(42, None), (pathlib.Path('tests'), 1), """
|
||||||
'''(pathlib.Path('tests/'), 1), (1, pathlib'''
|
"""(pathlib.Path('tests/'), 1), (1, pathlib"""
|
||||||
'''.Path('tests/')), (1, pathlib.Path('test'''
|
""".Path('tests/')), (1, pathlib.Path('test"""
|
||||||
'''s')), (None, pathlib.Path('tests'))])'''
|
"""s')), (None, pathlib.Path('tests'))])"""
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -280,40 +286,42 @@ def test_PythonDocumentationGenerator_collect_modules(
|
|||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
[(
|
[
|
||||||
'''pytest.mark.parametrize('python_documentation_gener'''
|
(
|
||||||
'''ator,expected', [(PythonDocumentationGenerator(path'''
|
"""pytest.mark.parametrize('python_documentation_gener"""
|
||||||
'''lib.Path('tests/unit/core/python/_core/test_project'''
|
"""ator,expected', [(PythonDocumentationGenerator(path"""
|
||||||
'''_dirs/pdir_1'), pathlib.Path('tests/unit/core/pytho'''
|
"""lib.Path('tests/unit/core/python/_core/test_project"""
|
||||||
'''n/_core/test_project_dirs/pdir_1/docs')), []), (Pyt'''
|
"""_dirs/pdir_1'), pathlib.Path('tests/unit/core/pytho"""
|
||||||
'''honDocumentationGenerator(pathlib.Path('tests/unit/'''
|
"""n/_core/test_project_dirs/pdir_1/docs')), []), (Pyt"""
|
||||||
'''core/python/_core/test_project_dirs/pdir_2'), pathl'''
|
"""honDocumentationGenerator(pathlib.Path('tests/unit/"""
|
||||||
'''ib.Path('tests/unit/core/python/_core/test_project_'''
|
"""core/python/_core/test_project_dirs/pdir_2'), pathl"""
|
||||||
'''dirs/pdir_2/docs')), [PythonPackageNamespace('pkg','''
|
"""ib.Path('tests/unit/core/python/_core/test_project_"""
|
||||||
''' [PythonModuleNamespace('module')])]), (PythonDocum'''
|
"""dirs/pdir_2/docs')), [PythonPackageNamespace('pkg',"""
|
||||||
'''entationGenerator(pathlib.Path('tests/unit/core/pyt'''
|
""" [PythonModuleNamespace('module')])]), (PythonDocum"""
|
||||||
'''hon/_core/test_project_dirs/pdir_3'), pathlib.Path('''
|
"""entationGenerator(pathlib.Path('tests/unit/core/pyt"""
|
||||||
''''tests/unit/core/python/_core/test_project_dirs/pdi'''
|
"""hon/_core/test_project_dirs/pdir_3'), pathlib.Path("""
|
||||||
'''r_3/docs')), [PythonPackageNamespace('pkg', [Python'''
|
"""'tests/unit/core/python/_core/test_project_dirs/pdi"""
|
||||||
'''ModuleNamespace(\'module'), PythonPackageNamespace('''
|
"""r_3/docs')), [PythonPackageNamespace('pkg', [Python"""
|
||||||
'''\'pkg\', [PythonModuleNamespace(\'module2')])])])])'''
|
"""ModuleNamespace(\'module'), PythonPackageNamespace("""
|
||||||
)],
|
"""\'pkg\', [PythonModuleNamespace(\'module2')])])])])"""
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
module(
|
module(
|
||||||
'''class C1:
|
"""class C1:
|
||||||
class C2:
|
class C2:
|
||||||
class C3:
|
class C3:
|
||||||
def function(self, value: bool, /, arg) -> bool:
|
def function(self, value: bool, /, arg) -> bool:
|
||||||
def should_be_not_in_the_result(arg: int) -> object:
|
def should_be_not_in_the_result(arg: int) -> object:
|
||||||
pass
|
pass
|
||||||
return not value'''
|
return not value"""
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
PythonClassDefinition(
|
PythonClassDefinition(
|
||||||
@@ -354,7 +362,7 @@ def test_PythonDocumentationGenerator_collect_modules(
|
|||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
'bool',
|
'bool',
|
||||||
None,
|
None,
|
||||||
@@ -368,6 +376,7 @@ def test_PythonDocumentationGenerator_collect_modules(
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test___collect_definitions(module, expected):
|
def test___collect_definitions(module, expected):
|
||||||
assert list(_collect_definitions(module)) == expected
|
assert list(_collect_definitions(module)) == expected
|
||||||
@@ -12,15 +12,18 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python._core import _fit_row_length
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.parametrize('expected_length,row,expected', [
|
from src.jcloud_docsgen.core.python._core import _fit_row_length
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'expected_length,row,expected',
|
||||||
|
[
|
||||||
(0, (), ()),
|
(0, (), ()),
|
||||||
(1, (), (None,)),
|
(1, (), (None,)),
|
||||||
(2, (), (None, None)),
|
(2, (), (None, None)),
|
||||||
(2, (1,), (1, None)),
|
(2, (1,), (1, None)),
|
||||||
|
|
||||||
(0, (1,), ()),
|
(0, (1,), ()),
|
||||||
(1, (1, 2), (1,)),
|
(1, (1, 2), (1,)),
|
||||||
(1, (1, 2), (1,)),
|
(1, (1, 2), (1,)),
|
||||||
@@ -28,28 +31,35 @@ import pytest
|
|||||||
(0, (1, 2, 3), ()),
|
(0, (1, 2, 3), ()),
|
||||||
(1, (1, 2, 3), (1,)),
|
(1, (1, 2, 3), (1,)),
|
||||||
(2, (1, 2, 3), (1, 2)),
|
(2, (1, 2, 3), (1, 2)),
|
||||||
|
|
||||||
(1, (1,), (1,)),
|
(1, (1,), (1,)),
|
||||||
(2, (1, 1), (1, 1)),
|
(2, (1, 1), (1, 1)),
|
||||||
(2, (1, 2), (1, 2)),
|
(2, (1, 2), (1, 2)),
|
||||||
(3, (1, 2, 3), (1, 2, 3)),
|
(3, (1, 2, 3), (1, 2, 3)),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test__fit_row_length(expected_length, row, expected):
|
def test__fit_row_length(expected_length, row, expected):
|
||||||
assert _fit_row_length(expected_length, row) == expected
|
assert _fit_row_length(expected_length, row) == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('row', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'row',
|
||||||
|
[
|
||||||
(),
|
(),
|
||||||
(1,),
|
(1,),
|
||||||
(1, 2),
|
(1, 2),
|
||||||
(1, 2, 3),
|
(1, 2, 3),
|
||||||
(1, 2, 3, 456),
|
(1, 2, 3, 456),
|
||||||
])
|
],
|
||||||
@pytest.mark.parametrize('expected_length,expected_exception', [
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'expected_length,expected_exception',
|
||||||
|
[
|
||||||
(-1, ValueError),
|
(-1, ValueError),
|
||||||
(-2, ValueError),
|
(-2, ValueError),
|
||||||
(-3, ValueError),
|
(-3, ValueError),
|
||||||
(-42, ValueError),
|
(-42, ValueError),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test__fit_row_length_exceptions(expected_length, row, expected_exception):
|
def test__fit_row_length_exceptions(expected_length, row, expected_exception):
|
||||||
with pytest.raises(expected_exception) as exc_info:
|
with pytest.raises(expected_exception) as exc_info:
|
||||||
_fit_row_length(expected_length, row)
|
_fit_row_length(expected_length, row)
|
||||||
|
|||||||
@@ -12,21 +12,23 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import docstring_parser
|
||||||
|
import pytest
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python._core import (
|
from src.jcloud_docsgen.core.python._core import (
|
||||||
_function_params_info,
|
_function_params_info,
|
||||||
_FunctionParamInfo,
|
_FunctionParamInfo,
|
||||||
)
|
)
|
||||||
from src.jcloud_docsgen.core.python.arguments import (
|
from src.jcloud_docsgen.core.python.arguments import (
|
||||||
PythonFunctionArgument,
|
|
||||||
PythonArgumentKind,
|
PythonArgumentKind,
|
||||||
|
PythonFunctionArgument,
|
||||||
)
|
)
|
||||||
import pytest
|
|
||||||
import docstring_parser
|
|
||||||
|
|
||||||
def docstring_args_list(
|
def docstring_args_list(
|
||||||
docstring: str,
|
docstring: str,
|
||||||
) -> list[docstring_parser.DocstringParam]:
|
) -> list[docstring_parser.DocstringParam]:
|
||||||
'''
|
"""
|
||||||
Returns the parameter list of a docstring.
|
Returns the parameter list of a docstring.
|
||||||
|
|
||||||
:param docstring: The docstring
|
:param docstring: The docstring
|
||||||
@@ -34,82 +36,164 @@ def docstring_args_list(
|
|||||||
|
|
||||||
:return: The parameter list.
|
:return: The parameter list.
|
||||||
:rtype: list[docstring_parser.DocstringParam]
|
:rtype: list[docstring_parser.DocstringParam]
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return docstring_parser.parse(docstring).params
|
return docstring_parser.parse(docstring).params
|
||||||
|
|
||||||
@pytest.mark.parametrize('arg_list,docstring,expected', [
|
|
||||||
([
|
@pytest.mark.parametrize(
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', 'int'),
|
'arg_list,docstring,expected',
|
||||||
], ':param a: a\n:type a: str', {
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', 'int'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a\n:type a: str',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('int', 'a'),
|
'a': _FunctionParamInfo('int', 'a'),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
], ':param a: a\n:type a: str', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a\n:type a: str',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', 'a'),
|
'a': _FunctionParamInfo('str', 'a'),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, '', None),
|
[
|
||||||
], ':param a: a\n:type a: str', {
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'b', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a\n:type a: str',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', 'a'),
|
'a': _FunctionParamInfo('str', 'a'),
|
||||||
'b': _FunctionParamInfo(None, None),
|
'b': _FunctionParamInfo(None, None),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, '', 'int'),
|
[
|
||||||
], ':param a: a\n:type a: str', {
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'b', PythonArgumentKind.NORMAL, '', 'int'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a\n:type a: str',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', 'a'),
|
'a': _FunctionParamInfo('str', 'a'),
|
||||||
'b': _FunctionParamInfo('int', None),
|
'b': _FunctionParamInfo('int', None),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, '', 'int'),
|
[
|
||||||
], ':param a: a\n:type a: str\n:param b: b\n:type b: str', {
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'b', PythonArgumentKind.NORMAL, '', 'int'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a\n:type a: str\n:param b: b\n:type b: str',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', 'a'),
|
'a': _FunctionParamInfo('str', 'a'),
|
||||||
'b': _FunctionParamInfo('int', 'b'),
|
'b': _FunctionParamInfo('int', 'b'),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
], ':param a: a\n:type a: str\n:param b: b\n:type b: int', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a\n:type a: str\n:param b: b\n:type b: int',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', 'a'),
|
'a': _FunctionParamInfo('str', 'a'),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
], '', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo(None, None),
|
'a': _FunctionParamInfo(None, None),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', 'str'),
|
(
|
||||||
], '', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', 'str'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', None),
|
'a': _FunctionParamInfo('str', None),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', 'str'),
|
(
|
||||||
], ':param a:\n:type a: int', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', 'str'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a:\n:type a: int',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', ''),
|
'a': _FunctionParamInfo('str', ''),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
], ':param a:\n:type a: int', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a:\n:type a: int',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('int', ''),
|
'a': _FunctionParamInfo('int', ''),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', None),
|
(
|
||||||
], ':param a: a', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', None
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo(None, 'a'),
|
'a': _FunctionParamInfo(None, 'a'),
|
||||||
}),
|
},
|
||||||
([
|
),
|
||||||
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, '', 'str'),
|
(
|
||||||
], ':param a: a', {
|
[
|
||||||
|
PythonFunctionArgument(
|
||||||
|
'a', PythonArgumentKind.NORMAL, '', 'str'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
':param a: a',
|
||||||
|
{
|
||||||
'a': _FunctionParamInfo('str', 'a'),
|
'a': _FunctionParamInfo('str', 'a'),
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
([], ':param a: a\n:type a: str', {}),
|
([], ':param a: a\n:type a: str', {}),
|
||||||
([], '', {}),
|
([], '', {}),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test__function_params_types(arg_list, docstring, expected):
|
def test__function_params_types(arg_list, docstring, expected):
|
||||||
assert _function_params_info(
|
assert (
|
||||||
arg_list,
|
_function_params_info(arg_list, docstring_args_list(docstring))
|
||||||
docstring_args_list(docstring)
|
== expected
|
||||||
) == expected
|
)
|
||||||
@@ -12,9 +12,11 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python._core import _markdown_table
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.core.python._core import _markdown_table
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
(
|
(
|
||||||
'header,body,allow_markdown_table,force_row_le'
|
'header,body,allow_markdown_table,force_row_le'
|
||||||
@@ -55,9 +57,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''| a |
|
"""| a |
|
||||||
| --- |
|
| --- |
|
||||||
| 1 |''',
|
| 1 |""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a'],
|
['a'],
|
||||||
@@ -66,9 +68,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''| a |
|
"""| a |
|
||||||
| --- |
|
| --- |
|
||||||
| 1 |''',
|
| 1 |""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
@@ -77,9 +79,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''| a | b |
|
"""| a | b |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 1 | 2 |''',
|
| 1 | 2 |""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
@@ -88,9 +90,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''| a | b |
|
"""| a | b |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 1 | -- |''',
|
| 1 | -- |""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
@@ -99,9 +101,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''| a | b |
|
"""| a | b |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| -- | -- |''',
|
| -- | -- |""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
@@ -110,10 +112,10 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''| a | b |
|
"""| a | b |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 1 | 2 |
|
| 1 | 2 |
|
||||||
| 3 | 4 |''',
|
| 3 | 4 |""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a'],
|
['a'],
|
||||||
@@ -122,9 +124,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''| a |
|
"""| a |
|
||||||
| --- |
|
| --- |
|
||||||
| line1 line2 |''',
|
| line1 line2 |""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a'],
|
['a'],
|
||||||
@@ -133,11 +135,10 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
False,
|
False,
|
||||||
'--',
|
'--',
|
||||||
'''| a |
|
"""| a |
|
||||||
| --- |
|
| --- |
|
||||||
| line1\nline2 |''',
|
| line1\nline2 |""",
|
||||||
),
|
),
|
||||||
|
|
||||||
(
|
(
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
@@ -190,9 +191,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''- a: 1
|
"""- a: 1
|
||||||
|
|
||||||
b: 2''',
|
b: 2""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
@@ -201,9 +202,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''- a: 1
|
"""- a: 1
|
||||||
|
|
||||||
b: --''',
|
b: --""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
@@ -212,9 +213,9 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''- a: --
|
"""- a: --
|
||||||
|
|
||||||
b: --''',
|
b: --""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
@@ -223,13 +224,13 @@ import pytest
|
|||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
'--',
|
'--',
|
||||||
'''- a: 1
|
"""- a: 1
|
||||||
|
|
||||||
b: 2
|
b: 2
|
||||||
|
|
||||||
- a: 3
|
- a: 3
|
||||||
|
|
||||||
b: 4''',
|
b: 4""",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
['a'],
|
['a'],
|
||||||
@@ -260,14 +261,18 @@ def test__markdown_table(
|
|||||||
missing_value,
|
missing_value,
|
||||||
expected,
|
expected,
|
||||||
):
|
):
|
||||||
assert _markdown_table(
|
assert (
|
||||||
|
_markdown_table(
|
||||||
header,
|
header,
|
||||||
body,
|
body,
|
||||||
allow_markdown_table=allow_markdown_table,
|
allow_markdown_table=allow_markdown_table,
|
||||||
force_row_lengths=force_row_lengths,
|
force_row_lengths=force_row_lengths,
|
||||||
one_line_values=one_line_values,
|
one_line_values=one_line_values,
|
||||||
missing_value=missing_value,
|
missing_value=missing_value,
|
||||||
) == expected
|
)
|
||||||
|
== expected
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
(
|
(
|
||||||
@@ -335,7 +340,6 @@ def test__markdown_table(
|
|||||||
ValueError,
|
ValueError,
|
||||||
'expected 2 values, got 3',
|
'expected 2 values, got 3',
|
||||||
),
|
),
|
||||||
|
|
||||||
(
|
(
|
||||||
['a'],
|
['a'],
|
||||||
[(1, 2)],
|
[(1, 2)],
|
||||||
|
|||||||
@@ -12,31 +12,42 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# 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,
|
|
||||||
)
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.parametrize('argument_list,expected', [
|
from src.jcloud_docsgen.core.python._core import _signature_argument_list
|
||||||
([
|
from src.jcloud_docsgen.core.python.arguments import (
|
||||||
|
PythonArgumentKind,
|
||||||
|
PythonFunctionArgument,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'argument_list,expected',
|
||||||
|
[
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a']),
|
],
|
||||||
([
|
['a'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '/']),
|
],
|
||||||
([
|
['a', '/'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
@@ -49,8 +60,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '/', 'b']),
|
],
|
||||||
([
|
['a', '/', 'b'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
@@ -69,8 +83,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '/', 'b', '*', 'c']),
|
],
|
||||||
([
|
['a', '/', 'b', '*', 'c'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'b',
|
'b',
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
@@ -83,16 +100,22 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['b', '*', 'c']),
|
],
|
||||||
([
|
['b', '*', 'c'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'c',
|
'c',
|
||||||
PythonArgumentKind.KEYWORD_ONLY,
|
PythonArgumentKind.KEYWORD_ONLY,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['*', 'c']),
|
],
|
||||||
([
|
['*', 'c'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'b',
|
'b',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
@@ -105,16 +128,22 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['b', '/', '*', 'c']),
|
],
|
||||||
([
|
['b', '/', '*', 'c'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'args',
|
'args',
|
||||||
PythonArgumentKind.VARARG,
|
PythonArgumentKind.VARARG,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
], ['*args']),
|
],
|
||||||
([
|
['*args'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
@@ -127,10 +156,14 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '*args']),
|
],
|
||||||
([
|
['a', '*args'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a', PythonArgumentKind.POSITIONAL_ONLY,
|
'a',
|
||||||
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
@@ -140,8 +173,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '/', '*args']),
|
],
|
||||||
([
|
['a', '/', '*args'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'args',
|
'args',
|
||||||
PythonArgumentKind.VARARG,
|
PythonArgumentKind.VARARG,
|
||||||
@@ -154,8 +190,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['*args', 'a']),
|
],
|
||||||
([
|
['*args', 'a'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'args',
|
'args',
|
||||||
PythonArgumentKind.VARARG,
|
PythonArgumentKind.VARARG,
|
||||||
@@ -168,8 +207,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['*args', '**kwargs']),
|
],
|
||||||
([
|
['*args', '**kwargs'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
@@ -188,8 +230,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '/', '*args', '**kwargs']),
|
],
|
||||||
([
|
['a', '/', '*args', '**kwargs'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
@@ -208,8 +253,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '*args', '**kwargs']),
|
],
|
||||||
([
|
['a', '*args', '**kwargs'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'args',
|
'args',
|
||||||
PythonArgumentKind.VARARG,
|
PythonArgumentKind.VARARG,
|
||||||
@@ -228,8 +276,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['*args', 'a', '**kwargs']),
|
],
|
||||||
([
|
['*args', 'a', '**kwargs'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.KEYWORD_ONLY,
|
PythonArgumentKind.KEYWORD_ONLY,
|
||||||
@@ -242,8 +293,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['*', 'a', '**kwargs']),
|
],
|
||||||
([
|
['*', 'a', '**kwargs'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
@@ -256,8 +310,11 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '**kwargs']),
|
],
|
||||||
([
|
['a', '**kwargs'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
@@ -270,15 +327,21 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
], ['a', '/', '**kwargs']),
|
],
|
||||||
([
|
['a', '/', '**kwargs'],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
'1',
|
'1',
|
||||||
'int',
|
'int',
|
||||||
),
|
),
|
||||||
], ['a: int = 1']),
|
],
|
||||||
])
|
['a: int = 1'],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test__signature_argument_list(argument_list, expected):
|
def test__signature_argument_list(argument_list, expected):
|
||||||
assert _signature_argument_list(argument_list) == expected
|
assert _signature_argument_list(argument_list) == expected
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
'''
|
"""
|
||||||
A test module.
|
A test module.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def add(a: int | float, b: int | float) -> int | float:
|
def add(a: int | float, b: int | float) -> int | float:
|
||||||
'''
|
"""
|
||||||
Adds two numbers.
|
Adds two numbers.
|
||||||
|
|
||||||
:param a: The first number.
|
:param a: The first number.
|
||||||
@@ -13,38 +14,41 @@ def add(a: int | float, b: int | float) -> int | float:
|
|||||||
|
|
||||||
:return: The sum.
|
:return: The sum.
|
||||||
:rtype: int | float
|
:rtype: int | float
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return a + b
|
return a + b
|
||||||
|
|
||||||
|
|
||||||
class Animal:
|
class Animal:
|
||||||
'''
|
"""
|
||||||
A class representing an animal.
|
A class representing an animal.
|
||||||
|
|
||||||
:param name: Its name.
|
:param name: Its name.
|
||||||
:type name: str
|
:type name: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def greet(self):
|
def greet(self):
|
||||||
'''
|
"""
|
||||||
Returns a greeting.
|
Returns a greeting.
|
||||||
|
|
||||||
:return: The greeting.
|
:return: The greeting.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return f'Hi there! My name is {self.name}'
|
return f'Hi there! My name is {self.name}'
|
||||||
|
|
||||||
|
|
||||||
class Dog(Animal):
|
class Dog(Animal):
|
||||||
'''
|
"""
|
||||||
A dog.
|
A dog.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def bark(self) -> None:
|
def bark(self) -> None:
|
||||||
'''
|
"""
|
||||||
Barks.
|
Barks.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
print('Woof!')
|
print('Woof!')
|
||||||
@@ -12,16 +12,19 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.arguments import (
|
|
||||||
PythonASTArgumentsListParser,
|
|
||||||
PythonFunctionArgument,
|
|
||||||
PythonArgumentKind,
|
|
||||||
)
|
|
||||||
import pytest
|
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.core.python.arguments import (
|
||||||
|
PythonArgumentKind,
|
||||||
|
PythonASTArgumentsListParser,
|
||||||
|
PythonFunctionArgument,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def argument_list(argument_list: str) -> ast.arguments:
|
def argument_list(argument_list: str) -> ast.arguments:
|
||||||
'''
|
"""
|
||||||
Returns an AST argument list from the argument list source code
|
Returns an AST argument list from the argument list source code
|
||||||
|
|
||||||
:param argument_list: The argument list.
|
:param argument_list: The argument list.
|
||||||
@@ -29,11 +32,14 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
|
|
||||||
:return: The AST argument list.
|
:return: The AST argument list.
|
||||||
:rtype: ast.arguments
|
:rtype: ast.arguments
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return ast.parse('def func(' + argument_list + '): ...').body[0].args
|
return ast.parse('def func(' + argument_list + '): ...').body[0].args
|
||||||
|
|
||||||
@pytest.mark.parametrize('ast_arguments_list,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'ast_arguments_list,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
argument_list('a'),
|
argument_list('a'),
|
||||||
[
|
[
|
||||||
@@ -679,8 +685,9 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
argument_list(
|
argument_list(
|
||||||
'a = 1, b = \'a\', /, *args, c = None, d = True, **kwargs',
|
"a = 1, b = 'a', /, *args, c = None, d = True, **kwargs",
|
||||||
), [
|
),
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
@@ -690,7 +697,7 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'b',
|
'b',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
'\'a\'',
|
"'a'",
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
@@ -720,7 +727,9 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
argument_list('a, b = \'a\', /, *args, c = None, d = True, **kwargs'),
|
argument_list(
|
||||||
|
"a, b = 'a', /, *args, c = None, d = True, **kwargs"
|
||||||
|
),
|
||||||
[
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
@@ -731,7 +740,7 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'b',
|
'b',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
'\'a\'',
|
"'a'",
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
@@ -741,10 +750,7 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'c',
|
'c', PythonArgumentKind.KEYWORD_ONLY, 'None', None
|
||||||
PythonArgumentKind.KEYWORD_ONLY,
|
|
||||||
'None',
|
|
||||||
None
|
|
||||||
),
|
),
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'd',
|
'd',
|
||||||
@@ -884,7 +890,8 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
argument_list('a, b , /, *args, c = None, d, **kwargs'), [
|
argument_list('a, b , /, *args, c = None, d, **kwargs'),
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
@@ -934,11 +941,13 @@ def argument_list(argument_list: str) -> ast.arguments:
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonASTArgumentsListParser_to_argument_list(
|
def test_PythonASTArgumentsListParser_to_argument_list(
|
||||||
ast_arguments_list,
|
ast_arguments_list,
|
||||||
expected,
|
expected,
|
||||||
):
|
):
|
||||||
assert PythonASTArgumentsListParser(
|
assert (
|
||||||
ast_arguments_list
|
PythonASTArgumentsListParser(ast_arguments_list).to_argument_list()
|
||||||
).to_argument_list() == expected
|
== expected
|
||||||
|
)
|
||||||
@@ -12,16 +12,18 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.arguments import (
|
from src.jcloud_docsgen.core.python.arguments import (
|
||||||
PythonFunctionArgument,
|
|
||||||
PythonArgumentKind,
|
PythonArgumentKind,
|
||||||
|
PythonFunctionArgument,
|
||||||
)
|
)
|
||||||
from src.jcloud_docsgen.exceptions import (
|
from src.jcloud_docsgen.exceptions import (
|
||||||
InvalidPythonIdentifierError,
|
|
||||||
InvalidPythonAnnotationError,
|
InvalidPythonAnnotationError,
|
||||||
|
InvalidPythonIdentifierError,
|
||||||
PythonArgumentStructureError,
|
PythonArgumentStructureError,
|
||||||
)
|
)
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'name,kind,default,annotation,expected_exception,expected_exception_msg',
|
'name,kind,default,annotation,expected_exception,expected_exception_msg',
|
||||||
@@ -32,7 +34,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected \'str\', got \'NoneType\'',
|
"expected 'str', got 'NoneType'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
@@ -40,7 +42,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected \'str\', got \'int\'',
|
"expected 'str', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
42,
|
42,
|
||||||
@@ -48,7 +50,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected \'str\', got \'int\'',
|
"expected 'str', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
4.2,
|
4.2,
|
||||||
@@ -56,7 +58,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected \'str\', got \'float\'',
|
"expected 'str', got 'float'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
4.2,
|
4.2,
|
||||||
@@ -64,7 +66,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected \'str\', got \'float\'',
|
"expected 'str', got 'float'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'a',
|
'a',
|
||||||
@@ -72,7 +74,7 @@ import pytest
|
|||||||
1,
|
1,
|
||||||
None,
|
None,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected either \'str\' or \'NoneType\', got \'int\'',
|
"expected either 'str' or 'NoneType', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'a',
|
'a',
|
||||||
@@ -80,7 +82,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
1,
|
1,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected either \'str\' or \'NoneType\', got \'int\'',
|
"expected either 'str' or 'NoneType', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'a',
|
'a',
|
||||||
@@ -88,7 +90,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
TypeError,
|
TypeError,
|
||||||
'expected \'PythonArgumentKind\', got \'int\'',
|
"expected 'PythonArgumentKind', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'a',
|
'a',
|
||||||
@@ -384,7 +386,10 @@ def test_PythonFunctionArgument_exceptions(
|
|||||||
PythonFunctionArgument(name, kind, default, annotation)
|
PythonFunctionArgument(name, kind, default, annotation)
|
||||||
assert str(exc_info.value) == expected_exception_msg
|
assert str(exc_info.value) == expected_exception_msg
|
||||||
|
|
||||||
@pytest.mark.parametrize('name', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'name',
|
||||||
|
[
|
||||||
'a',
|
'a',
|
||||||
'_a',
|
'_a',
|
||||||
'a1',
|
'a1',
|
||||||
@@ -401,28 +406,41 @@ def test_PythonFunctionArgument_exceptions(
|
|||||||
'none',
|
'none',
|
||||||
'true',
|
'true',
|
||||||
'false',
|
'false',
|
||||||
])
|
],
|
||||||
@pytest.mark.parametrize('kind', [
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'kind',
|
||||||
|
[
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
PythonArgumentKind.POSITIONAL_ONLY,
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
PythonArgumentKind.KEYWORD_ONLY,
|
PythonArgumentKind.KEYWORD_ONLY,
|
||||||
])
|
],
|
||||||
@pytest.mark.parametrize('default', [
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'default',
|
||||||
|
[
|
||||||
None,
|
None,
|
||||||
'x',
|
'x',
|
||||||
'1',
|
'1',
|
||||||
None,
|
None,
|
||||||
])
|
],
|
||||||
@pytest.mark.parametrize('annotation', [
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'annotation',
|
||||||
|
[
|
||||||
None,
|
None,
|
||||||
'a',
|
'a',
|
||||||
'list[int]',
|
'list[int]',
|
||||||
'_',
|
'_',
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonFunctionArgument(name, kind, default, annotation):
|
def test_PythonFunctionArgument(name, kind, default, annotation):
|
||||||
PythonFunctionArgument(name, kind, default, annotation)
|
PythonFunctionArgument(name, kind, default, annotation)
|
||||||
|
|
||||||
@pytest.mark.parametrize('name', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'name',
|
||||||
|
[
|
||||||
'a',
|
'a',
|
||||||
'_a',
|
'_a',
|
||||||
'a1',
|
'a1',
|
||||||
@@ -439,21 +457,31 @@ def test_PythonFunctionArgument(name, kind, default, annotation):
|
|||||||
'none',
|
'none',
|
||||||
'true',
|
'true',
|
||||||
'false',
|
'false',
|
||||||
])
|
],
|
||||||
@pytest.mark.parametrize('kind', [
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'kind',
|
||||||
|
[
|
||||||
PythonArgumentKind.VARARG,
|
PythonArgumentKind.VARARG,
|
||||||
PythonArgumentKind.KWARGS,
|
PythonArgumentKind.KWARGS,
|
||||||
])
|
],
|
||||||
@pytest.mark.parametrize('annotation', [
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'annotation',
|
||||||
|
[
|
||||||
None,
|
None,
|
||||||
'a',
|
'a',
|
||||||
'list[int]',
|
'list[int]',
|
||||||
'_',
|
'_',
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonFunctionArgument_asterik(name, kind, annotation):
|
def test_PythonFunctionArgument_asterik(name, kind, annotation):
|
||||||
PythonFunctionArgument(name, kind, None, annotation)
|
PythonFunctionArgument(name, kind, None, annotation)
|
||||||
|
|
||||||
@pytest.mark.parametrize('argument,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'argument,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
@@ -474,10 +502,7 @@ def test_PythonFunctionArgument_asterik(name, kind, annotation):
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a', PythonArgumentKind.NORMAL, None, 'int'
|
||||||
PythonArgumentKind.NORMAL,
|
|
||||||
None,
|
|
||||||
'int'
|
|
||||||
),
|
),
|
||||||
'a: int',
|
'a: int',
|
||||||
),
|
),
|
||||||
@@ -598,6 +623,7 @@ def test_PythonFunctionArgument_asterik(name, kind, annotation):
|
|||||||
),
|
),
|
||||||
'**a: int',
|
'**a: int',
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonFunctionArgument_signature_repr(argument, expected):
|
def test_PythonFunctionArgument_signature_repr(argument, expected):
|
||||||
assert argument.signature_repr() == expected
|
assert argument.signature_repr() == expected
|
||||||
@@ -12,20 +12,24 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.definitions import (
|
import pytest
|
||||||
PythonFunctionDefinition,
|
|
||||||
PythonClassDefinition,
|
|
||||||
)
|
|
||||||
from src.jcloud_docsgen.core.python.arguments import (
|
from src.jcloud_docsgen.core.python.arguments import (
|
||||||
PythonFunctionArgument,
|
|
||||||
PythonArgumentKind,
|
PythonArgumentKind,
|
||||||
|
PythonFunctionArgument,
|
||||||
|
)
|
||||||
|
from src.jcloud_docsgen.core.python.definitions import (
|
||||||
|
PythonClassDefinition,
|
||||||
|
PythonFunctionDefinition,
|
||||||
)
|
)
|
||||||
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
||||||
from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError
|
from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError
|
||||||
from tests.utils.ast_node import ast_node
|
from tests.utils.ast_node import ast_node
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('definition,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'definition,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonClassDefinition(
|
PythonClassDefinition(
|
||||||
'A',
|
'A',
|
||||||
@@ -34,7 +38,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
'PythonClassDefinition(\'A\', [], None, None, [])',
|
"PythonClassDefinition('A', [], None, None, [])",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
PythonClassDefinition(
|
PythonClassDefinition(
|
||||||
@@ -45,11 +49,12 @@ import pytest
|
|||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'PythonClassDefinition(\'A\', [], None, '
|
"PythonClassDefinition('A', [], None, "
|
||||||
'PythonDocstring(\'Hello, World!\'), [])'
|
"PythonDocstring('Hello, World!'), [])"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonDefinition_string_representation(definition, expected):
|
def test_PythonDefinition_string_representation(definition, expected):
|
||||||
assert repr(definition) == expected
|
assert repr(definition) == expected
|
||||||
|
|
||||||
@@ -364,7 +369,9 @@ def test_PythonFunctionDefinition_exceptions(
|
|||||||
assert str(exc_info.value) == expected_exception_message
|
assert str(exc_info.value) == expected_exception_message
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('node,expected', [
|
@pytest.mark.parametrize(
|
||||||
|
'node,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
ast_node(
|
ast_node(
|
||||||
(
|
(
|
||||||
@@ -428,7 +435,7 @@ def test_PythonFunctionDefinition_exceptions(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ast_node('class Class: \'\'\'docstring\'\'\''),
|
ast_node("class Class: '''docstring'''"),
|
||||||
PythonClassDefinition(
|
PythonClassDefinition(
|
||||||
'Class',
|
'Class',
|
||||||
[],
|
[],
|
||||||
@@ -463,7 +470,7 @@ def test_PythonFunctionDefinition_exceptions(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ast_node('class Class:\n\t\'\'\'doc\'\'\'\n\tdef __init__(self): ...'),
|
ast_node("class Class:\n\t'''doc'''\n\tdef __init__(self): ..."),
|
||||||
PythonClassDefinition(
|
PythonClassDefinition(
|
||||||
'Class',
|
'Class',
|
||||||
[],
|
[],
|
||||||
@@ -487,6 +494,7 @@ def test_PythonFunctionDefinition_exceptions(
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonDefinition_from_node(node, expected):
|
def test_PythonDefinition_from_node(node, expected):
|
||||||
assert PythonClassDefinition.from_node(node) == expected
|
assert PythonClassDefinition.from_node(node) == expected
|
||||||
@@ -12,19 +12,23 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.definitions import PythonDefinition
|
from src.jcloud_docsgen.core.python.definitions import PythonDefinition
|
||||||
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
||||||
from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError
|
from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('definition,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'definition,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonDefinition(
|
PythonDefinition(
|
||||||
'a',
|
'a',
|
||||||
None,
|
None,
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
'PythonDefinition(\'a\', None, [])',
|
"PythonDefinition('a', None, [])",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
PythonDefinition(
|
PythonDefinition(
|
||||||
@@ -32,9 +36,10 @@ import pytest
|
|||||||
PythonDocstring('Hello, World!'),
|
PythonDocstring('Hello, World!'),
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
'PythonDefinition(\'a\', PythonDocstring(\'Hello, World!\'), [])',
|
"PythonDefinition('a', PythonDocstring('Hello, World!'), [])",
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonDefinition_string_representation(definition, expected):
|
def test_PythonDefinition_string_representation(definition, expected):
|
||||||
assert repr(definition) == expected
|
assert repr(definition) == expected
|
||||||
|
|
||||||
@@ -280,7 +285,9 @@ def test_PythonDefinition_exceptions(
|
|||||||
assert str(exc_info.value) == expected_exception_message
|
assert str(exc_info.value) == expected_exception_message
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('definition1,definition2,expected', [
|
@pytest.mark.parametrize(
|
||||||
|
'definition1,definition2,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonDefinition(
|
PythonDefinition(
|
||||||
'a',
|
'a',
|
||||||
@@ -437,7 +444,8 @@ def test_PythonDefinition_exceptions(
|
|||||||
),
|
),
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonDefinition___eq__(definition1, definition2, expected):
|
def test_PythonDefinition___eq__(definition1, definition2, expected):
|
||||||
assert (definition1 == definition2) == expected
|
assert (definition1 == definition2) == expected
|
||||||
assert (definition2 == definition1) == expected
|
assert (definition2 == definition1) == expected
|
||||||
@@ -12,17 +12,21 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.definitions import PythonFunctionDefinition
|
import pytest
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.arguments import (
|
from src.jcloud_docsgen.core.python.arguments import (
|
||||||
PythonFunctionArgument,
|
|
||||||
PythonArgumentKind,
|
PythonArgumentKind,
|
||||||
|
PythonFunctionArgument,
|
||||||
)
|
)
|
||||||
|
from src.jcloud_docsgen.core.python.definitions import PythonFunctionDefinition
|
||||||
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
from src.jcloud_docsgen.core.python.docstrings import PythonDocstring
|
||||||
from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError
|
from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError
|
||||||
from tests.utils.ast_node import ast_node
|
from tests.utils.ast_node import ast_node
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('definition,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'definition,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonFunctionDefinition(
|
PythonFunctionDefinition(
|
||||||
'a',
|
'a',
|
||||||
@@ -31,7 +35,7 @@ import pytest
|
|||||||
None,
|
None,
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
'PythonFunctionDefinition(\'a\', [], None, None, [])',
|
"PythonFunctionDefinition('a', [], None, None, [])",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
PythonFunctionDefinition(
|
PythonFunctionDefinition(
|
||||||
@@ -42,11 +46,12 @@ import pytest
|
|||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'PythonFunctionDefinition(\'a\', [], None,'
|
"PythonFunctionDefinition('a', [], None,"
|
||||||
' PythonDocstring(\'Hello, World!\'), [])'
|
" PythonDocstring('Hello, World!'), [])"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonDefinition_string_representation(definition, expected):
|
def test_PythonDefinition_string_representation(definition, expected):
|
||||||
assert repr(definition) == expected
|
assert repr(definition) == expected
|
||||||
|
|
||||||
@@ -345,7 +350,8 @@ def test_PythonDefinition_string_representation(definition, expected):
|
|||||||
InvalidPythonIdentifierError,
|
InvalidPythonIdentifierError,
|
||||||
'invalid identifier: True',
|
'invalid identifier: True',
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonFunctionDefinition_exceptions(
|
def test_PythonFunctionDefinition_exceptions(
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
@@ -360,11 +366,14 @@ def test_PythonFunctionDefinition_exceptions(
|
|||||||
assert str(exc_info.value) == expected_exception_message
|
assert str(exc_info.value) == expected_exception_message
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('node,expected', [
|
@pytest.mark.parametrize(
|
||||||
|
'node,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
ast_node('def add(a: int, b: int) -> int: return a + b'),
|
ast_node('def add(a: int, b: int) -> int: return a + b'),
|
||||||
PythonFunctionDefinition(
|
PythonFunctionDefinition(
|
||||||
'add', [
|
'add',
|
||||||
|
[
|
||||||
PythonFunctionArgument(
|
PythonFunctionArgument(
|
||||||
'a',
|
'a',
|
||||||
PythonArgumentKind.NORMAL,
|
PythonArgumentKind.NORMAL,
|
||||||
@@ -384,7 +393,9 @@ def test_PythonFunctionDefinition_exceptions(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ast_node('@decorator\ndef add(a: int, b: int) -> int: return a + b'),
|
ast_node(
|
||||||
|
'@decorator\ndef add(a: int, b: int) -> int: return a + b'
|
||||||
|
),
|
||||||
PythonFunctionDefinition(
|
PythonFunctionDefinition(
|
||||||
'add',
|
'add',
|
||||||
[
|
[
|
||||||
@@ -454,6 +465,7 @@ def test_PythonFunctionDefinition_exceptions(
|
|||||||
['decorator'],
|
['decorator'],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonDefinition_from_node(node, expected):
|
def test_PythonDefinition_from_node(node, expected):
|
||||||
assert PythonFunctionDefinition.from_node(node) == expected
|
assert PythonFunctionDefinition.from_node(node) == expected
|
||||||
@@ -12,20 +12,29 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.namespaces import PythonModuleNamespace
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.core.python.namespaces import PythonModuleNamespace
|
||||||
|
|
||||||
|
|
||||||
class StrSubclass(str): ...
|
class StrSubclass(str): ...
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,expected_name', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace,expected_name',
|
||||||
|
[
|
||||||
(PythonModuleNamespace('a'), 'a'),
|
(PythonModuleNamespace('a'), 'a'),
|
||||||
(PythonModuleNamespace('1'), '1'),
|
(PythonModuleNamespace('1'), '1'),
|
||||||
(PythonModuleNamespace(StrSubclass('a')), StrSubclass('a')),
|
(PythonModuleNamespace(StrSubclass('a')), StrSubclass('a')),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonModuleNamespace_name_attribute(namespace, expected_name):
|
def test_PythonModuleNamespace_name_attribute(namespace, expected_name):
|
||||||
assert namespace.name == expected_name
|
assert namespace.name == expected_name
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace1,namespace2,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace1,namespace2,expected',
|
||||||
|
[
|
||||||
(PythonModuleNamespace('a'), PythonModuleNamespace('a'), True),
|
(PythonModuleNamespace('a'), PythonModuleNamespace('a'), True),
|
||||||
(PythonModuleNamespace('b'), PythonModuleNamespace('b'), True),
|
(PythonModuleNamespace('b'), PythonModuleNamespace('b'), True),
|
||||||
(PythonModuleNamespace('1'), PythonModuleNamespace('1'), True),
|
(PythonModuleNamespace('1'), PythonModuleNamespace('1'), True),
|
||||||
@@ -33,15 +42,23 @@ def test_PythonModuleNamespace_name_attribute(namespace, expected_name):
|
|||||||
(PythonModuleNamespace('a'), PythonModuleNamespace('b'), False),
|
(PythonModuleNamespace('a'), PythonModuleNamespace('b'), False),
|
||||||
(PythonModuleNamespace('0'), PythonModuleNamespace('-0'), False),
|
(PythonModuleNamespace('0'), PythonModuleNamespace('-0'), False),
|
||||||
(PythonModuleNamespace('1'), PythonModuleNamespace('a'), False),
|
(PythonModuleNamespace('1'), PythonModuleNamespace('a'), False),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonModuleNamespace___eq__(namespace1, namespace2, expected):
|
def test_PythonModuleNamespace___eq__(namespace1, namespace2, expected):
|
||||||
assert (namespace1 == namespace2) == expected
|
assert (namespace1 == namespace2) == expected
|
||||||
assert (namespace2 == namespace1) == expected
|
assert (namespace2 == namespace1) == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,expected', [
|
|
||||||
(PythonModuleNamespace('a'), 'PythonModuleNamespace(\'a\')'),
|
@pytest.mark.parametrize(
|
||||||
(PythonModuleNamespace('1'), 'PythonModuleNamespace(\'1\')'),
|
'namespace,expected',
|
||||||
(PythonModuleNamespace(StrSubclass('a')), 'PythonModuleNamespace(\'a\')'),
|
[
|
||||||
])
|
(PythonModuleNamespace('a'), "PythonModuleNamespace('a')"),
|
||||||
|
(PythonModuleNamespace('1'), "PythonModuleNamespace('1')"),
|
||||||
|
(
|
||||||
|
PythonModuleNamespace(StrSubclass('a')),
|
||||||
|
"PythonModuleNamespace('a')",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_PythonModuleNamespace_string_representation(namespace, expected):
|
def test_PythonModuleNamespace_string_representation(namespace, expected):
|
||||||
assert repr(namespace) == expected
|
assert repr(namespace) == expected
|
||||||
@@ -12,23 +12,33 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.namespaces import PythonNamespace
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.core.python.namespaces import PythonNamespace
|
||||||
|
|
||||||
|
|
||||||
class StrSubclass(str): ...
|
class StrSubclass(str): ...
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,expected_name', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace,expected_name',
|
||||||
|
[
|
||||||
(PythonNamespace('a'), 'a'),
|
(PythonNamespace('a'), 'a'),
|
||||||
(PythonNamespace('1'), '1'),
|
(PythonNamespace('1'), '1'),
|
||||||
(PythonNamespace(StrSubclass('a')), StrSubclass('a')),
|
(PythonNamespace(StrSubclass('a')), StrSubclass('a')),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonNamespace_name_attribute(namespace, expected_name):
|
def test_PythonNamespace_name_attribute(namespace, expected_name):
|
||||||
assert namespace.name == expected_name
|
assert namespace.name == expected_name
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,expected', [
|
|
||||||
(PythonNamespace('a'), 'PythonNamespace(\'a\')'),
|
@pytest.mark.parametrize(
|
||||||
(PythonNamespace('1'), 'PythonNamespace(\'1\')'),
|
'namespace,expected',
|
||||||
(PythonNamespace(StrSubclass('a')), 'PythonNamespace(\'a\')'),
|
[
|
||||||
])
|
(PythonNamespace('a'), "PythonNamespace('a')"),
|
||||||
|
(PythonNamespace('1'), "PythonNamespace('1')"),
|
||||||
|
(PythonNamespace(StrSubclass('a')), "PythonNamespace('a')"),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_PythonNamespace_string_representation(namespace, expected):
|
def test_PythonNamespace_string_representation(namespace, expected):
|
||||||
assert repr(namespace) == expected
|
assert repr(namespace) == expected
|
||||||
@@ -12,19 +12,24 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.core.python.namespaces import (
|
|
||||||
PythonPackageNamespace,
|
|
||||||
PythonModuleNamespace,
|
|
||||||
)
|
|
||||||
import pytest
|
import pytest
|
||||||
from src.jcloud_docsgen.exceptions import (
|
|
||||||
NamespaceNotFoundError,
|
from src.jcloud_docsgen.core.python.namespaces import (
|
||||||
InvalidNamespaceError,
|
PythonModuleNamespace,
|
||||||
|
PythonPackageNamespace,
|
||||||
)
|
)
|
||||||
|
from src.jcloud_docsgen.exceptions import (
|
||||||
|
InvalidNamespaceError,
|
||||||
|
NamespaceNotFoundError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class StrSubclass(str): ...
|
class StrSubclass(str): ...
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,expected_name', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace,expected_name',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonPackageNamespace(
|
PythonPackageNamespace(
|
||||||
'a',
|
'a',
|
||||||
@@ -46,14 +51,19 @@ class StrSubclass(str): ...
|
|||||||
),
|
),
|
||||||
StrSubclass('a'),
|
StrSubclass('a'),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonPackageNamespace_name_attribute(namespace, expected_name):
|
def test_PythonPackageNamespace_name_attribute(namespace, expected_name):
|
||||||
assert namespace.name == expected_name
|
assert namespace.name == expected_name
|
||||||
|
|
||||||
|
|
||||||
namespace_b = PythonModuleNamespace('b')
|
namespace_b = PythonModuleNamespace('b')
|
||||||
namespace_a = PythonPackageNamespace('a', [
|
namespace_a = PythonPackageNamespace(
|
||||||
|
'a',
|
||||||
|
[
|
||||||
namespace_b,
|
namespace_b,
|
||||||
])
|
],
|
||||||
|
)
|
||||||
namespace_c = PythonModuleNamespace('c')
|
namespace_c = PythonModuleNamespace('c')
|
||||||
namespace_k = PythonModuleNamespace('k')
|
namespace_k = PythonModuleNamespace('k')
|
||||||
namespace_j = PythonPackageNamespace('j', [namespace_k])
|
namespace_j = PythonPackageNamespace('j', [namespace_k])
|
||||||
@@ -64,7 +74,10 @@ namespace_f = PythonPackageNamespace('f', [namespace_g])
|
|||||||
namespace_e = PythonPackageNamespace('e', [namespace_f])
|
namespace_e = PythonPackageNamespace('e', [namespace_f])
|
||||||
namespace_d = PythonPackageNamespace('d', [namespace_e])
|
namespace_d = PythonPackageNamespace('d', [namespace_e])
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,sub_namespaces,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace,sub_namespaces,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonPackageNamespace(
|
PythonPackageNamespace(
|
||||||
'name',
|
'name',
|
||||||
@@ -104,7 +117,8 @@ namespace_d = PythonPackageNamespace('d', [namespace_e])
|
|||||||
['e', 'f', 'g', 'h', 'i', 'j', 'k'],
|
['e', 'f', 'g', 'h', 'i', 'j', 'k'],
|
||||||
namespace_k,
|
namespace_k,
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonPackageNamespace_namespace(
|
def test_PythonPackageNamespace_namespace(
|
||||||
namespace: PythonPackageNamespace,
|
namespace: PythonPackageNamespace,
|
||||||
sub_namespaces,
|
sub_namespaces,
|
||||||
@@ -112,6 +126,7 @@ def test_PythonPackageNamespace_namespace(
|
|||||||
):
|
):
|
||||||
assert namespace.namespace(sub_namespaces) == expected
|
assert namespace.namespace(sub_namespaces) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'namespace,sub_namespaces,expected_exception,expected_exception_msg',
|
'namespace,sub_namespaces,expected_exception,expected_exception_msg',
|
||||||
[
|
[
|
||||||
@@ -157,7 +172,10 @@ def test_PythonPackageNamespace_namespace_exceptions(
|
|||||||
namespace.namespace(sub_namespaces)
|
namespace.namespace(sub_namespaces)
|
||||||
assert str(exc_info.value) == expected_exception_msg
|
assert str(exc_info.value) == expected_exception_msg
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonPackageNamespace(
|
PythonPackageNamespace(
|
||||||
'a',
|
'a',
|
||||||
@@ -185,11 +203,15 @@ def test_PythonPackageNamespace_namespace_exceptions(
|
|||||||
PythonPackageNamespace('a', []),
|
PythonPackageNamespace('a', []),
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonPackageNamespace_sub_namespace_names(namespace, expected):
|
def test_PythonPackageNamespace_sub_namespace_names(namespace, expected):
|
||||||
assert namespace.sub_namespace_names() == expected
|
assert namespace.sub_namespace_names() == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace1,namespace2,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace1,namespace2,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonPackageNamespace(
|
PythonPackageNamespace(
|
||||||
'a',
|
'a',
|
||||||
@@ -234,43 +256,51 @@ def test_PythonPackageNamespace_sub_namespace_names(namespace, expected):
|
|||||||
PythonPackageNamespace('b', []),
|
PythonPackageNamespace('b', []),
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonPackageNamespace___eq__(namespace1, namespace2, expected):
|
def test_PythonPackageNamespace___eq__(namespace1, namespace2, expected):
|
||||||
assert (namespace1 == namespace2) == expected
|
assert (namespace1 == namespace2) == expected
|
||||||
assert (namespace2 == namespace1) == expected
|
assert (namespace2 == namespace1) == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonPackageNamespace('a', []),
|
PythonPackageNamespace('a', []),
|
||||||
'PythonPackageNamespace(\'a\', [])',
|
"PythonPackageNamespace('a', [])",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
PythonPackageNamespace('1', []),
|
PythonPackageNamespace('1', []),
|
||||||
'PythonPackageNamespace(\'1\', [])',
|
"PythonPackageNamespace('1', [])",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
PythonPackageNamespace(StrSubclass('a'), []),
|
PythonPackageNamespace(StrSubclass('a'), []),
|
||||||
'PythonPackageNamespace(\'a\', [])',
|
"PythonPackageNamespace('a', [])",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
namespace_a,
|
namespace_a,
|
||||||
'PythonPackageNamespace(\'a\', [PythonModuleNamespace(\'b\')])',
|
"PythonPackageNamespace('a', [PythonModuleNamespace('b')])",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
namespace_d,
|
namespace_d,
|
||||||
(
|
(
|
||||||
'PythonPackageNamespace(\'d\', [PythonPackageNamespac'
|
"PythonPackageNamespace('d', [PythonPackageNamespac"
|
||||||
'e(\'e\', [PythonPackageNamespace(\'f\', [PythonPacka'
|
"e('e', [PythonPackageNamespace('f', [PythonPacka"
|
||||||
'geNamespace(\'g\', [PythonPackageNamespace(\'h\', [P'
|
"geNamespace('g', [PythonPackageNamespace('h', [P"
|
||||||
'ythonPackageNamespace(\'i\', [PythonPackageNamespace'
|
"ythonPackageNamespace('i', [PythonPackageNamespace"
|
||||||
'(\'j\', [PythonModuleNamespace(\'k\')])])])])])])])'
|
"('j', [PythonModuleNamespace('k')])])])])])])])"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonPackageNamespace_string_representation(namespace, expected):
|
def test_PythonPackageNamespace_string_representation(namespace, expected):
|
||||||
assert repr(namespace) == expected
|
assert repr(namespace) == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('namespace,new_namespace,expected', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'namespace,new_namespace,expected',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
PythonPackageNamespace('a', []),
|
PythonPackageNamespace('a', []),
|
||||||
PythonModuleNamespace('b'),
|
PythonModuleNamespace('b'),
|
||||||
@@ -279,7 +309,8 @@ def test_PythonPackageNamespace_string_representation(namespace, expected):
|
|||||||
[PythonModuleNamespace('b')],
|
[PythonModuleNamespace('b')],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_PythonPackageNamespace___getitem__(
|
def test_PythonPackageNamespace___getitem__(
|
||||||
namespace,
|
namespace,
|
||||||
new_namespace,
|
new_namespace,
|
||||||
|
|||||||
@@ -12,51 +12,64 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.utils import assert_that_is_instance
|
|
||||||
import pytest
|
|
||||||
import types
|
import types
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.utils import assert_that_is_instance
|
||||||
|
|
||||||
|
|
||||||
class TestType: ...
|
class TestType: ...
|
||||||
|
|
||||||
|
|
||||||
TestType.__name__ = 'NOT TestType'
|
TestType.__name__ = 'NOT TestType'
|
||||||
|
|
||||||
@pytest.mark.parametrize('obj,class_or_tuple', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'obj,class_or_tuple',
|
||||||
|
[
|
||||||
(1, int),
|
(1, int),
|
||||||
(1, (int,)),
|
(1, (int,)),
|
||||||
(1, str | int),
|
(1, str | int),
|
||||||
(1, int | str | list),
|
(1, int | str | list),
|
||||||
(TestType(), TestType),
|
(TestType(), TestType),
|
||||||
(None, types.NoneType),
|
(None, types.NoneType),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_assert_that_is_instance(obj, class_or_tuple):
|
def test_assert_that_is_instance(obj, class_or_tuple):
|
||||||
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', [
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'obj,class_or_tuple,expected_exception_msg',
|
||||||
|
[
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
str,
|
str,
|
||||||
'expected \'str\', got \'int\'',
|
"expected 'str', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
(str,),
|
(str,),
|
||||||
'expected \'str\', got \'int\'',
|
"expected 'str', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
str | float,
|
str | float,
|
||||||
'expected either \'str\' or \'float\', got \'int\'',
|
"expected either 'str' or 'float', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
str | float | list,
|
str | float | list,
|
||||||
'expected either \'str\', \'float\' or \'list\', got \'int\'',
|
"expected either 'str', 'float' or 'list', got 'int'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
TestType,
|
TestType,
|
||||||
'expected \'NOT TestType\', got \'int\'',
|
"expected 'NOT TestType', got 'int'",
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_assert_that_is_instance_exceptions(
|
def test_assert_that_is_instance_exceptions(
|
||||||
obj,
|
obj,
|
||||||
class_or_tuple,
|
class_or_tuple,
|
||||||
|
|||||||
@@ -12,11 +12,16 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.utils import get_relative_path_segments
|
|
||||||
import pytest
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
@pytest.mark.parametrize('path,root,expected', [
|
import pytest
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.utils import get_relative_path_segments
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'path,root,expected',
|
||||||
|
[
|
||||||
(pathlib.Path('/'), pathlib.Path('/'), []),
|
(pathlib.Path('/'), pathlib.Path('/'), []),
|
||||||
(pathlib.Path('/a'), pathlib.Path('/'), ['a']),
|
(pathlib.Path('/a'), pathlib.Path('/'), ['a']),
|
||||||
(pathlib.Path('/a/'), pathlib.Path('/'), ['a']),
|
(pathlib.Path('/a/'), pathlib.Path('/'), ['a']),
|
||||||
@@ -34,7 +39,6 @@ import pathlib
|
|||||||
(pathlib.Path('/a/b/'), pathlib.Path('/a/b'), []),
|
(pathlib.Path('/a/b/'), pathlib.Path('/a/b'), []),
|
||||||
(pathlib.Path('/a/b'), pathlib.Path('/a/b/'), []),
|
(pathlib.Path('/a/b'), pathlib.Path('/a/b/'), []),
|
||||||
(pathlib.Path('/a/b/'), pathlib.Path('/a/b/'), []),
|
(pathlib.Path('/a/b/'), pathlib.Path('/a/b/'), []),
|
||||||
|
|
||||||
(pathlib.Path('./'), pathlib.Path('./'), []),
|
(pathlib.Path('./'), pathlib.Path('./'), []),
|
||||||
(pathlib.Path('./a'), pathlib.Path('./'), ['a']),
|
(pathlib.Path('./a'), pathlib.Path('./'), ['a']),
|
||||||
(pathlib.Path('./a/'), pathlib.Path('./'), ['a']),
|
(pathlib.Path('./a/'), pathlib.Path('./'), ['a']),
|
||||||
@@ -52,7 +56,6 @@ import pathlib
|
|||||||
(pathlib.Path('./a/b/'), pathlib.Path('./a/b'), []),
|
(pathlib.Path('./a/b/'), pathlib.Path('./a/b'), []),
|
||||||
(pathlib.Path('./a/b'), pathlib.Path('./a/b/'), []),
|
(pathlib.Path('./a/b'), pathlib.Path('./a/b/'), []),
|
||||||
(pathlib.Path('./a/b/'), pathlib.Path('./a/b/'), []),
|
(pathlib.Path('./a/b/'), pathlib.Path('./a/b/'), []),
|
||||||
|
|
||||||
(pathlib.Path(''), pathlib.Path(''), []),
|
(pathlib.Path(''), pathlib.Path(''), []),
|
||||||
(pathlib.Path('a'), pathlib.Path(''), ['a']),
|
(pathlib.Path('a'), pathlib.Path(''), ['a']),
|
||||||
(pathlib.Path('a/'), pathlib.Path(''), ['a']),
|
(pathlib.Path('a/'), pathlib.Path(''), ['a']),
|
||||||
@@ -70,7 +73,7 @@ import pathlib
|
|||||||
(pathlib.Path('a/b/'), pathlib.Path('a/b'), []),
|
(pathlib.Path('a/b/'), pathlib.Path('a/b'), []),
|
||||||
(pathlib.Path('a/b'), pathlib.Path('a/b/'), []),
|
(pathlib.Path('a/b'), pathlib.Path('a/b/'), []),
|
||||||
(pathlib.Path('a/b/'), pathlib.Path('a/b/'), []),
|
(pathlib.Path('a/b/'), pathlib.Path('a/b/'), []),
|
||||||
|
],
|
||||||
])
|
)
|
||||||
def test_get_relative_path_segments(path, root, expected):
|
def test_get_relative_path_segments(path, root, expected):
|
||||||
assert get_relative_path_segments(path, root) == expected
|
assert get_relative_path_segments(path, root) == expected
|
||||||
@@ -12,13 +12,17 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.utils import human_readable_list
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.parametrize('ls,final_separator,quotation_mark,expected', [
|
from src.jcloud_docsgen.utils import human_readable_list
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'ls,final_separator,quotation_mark,expected',
|
||||||
|
[
|
||||||
([], '', '', ''),
|
([], '', '', ''),
|
||||||
([], 'and', '', ''),
|
([], 'and', '', ''),
|
||||||
([], 'and', '\'', ''),
|
([], 'and', "'", ''),
|
||||||
([1], '', '', '1'),
|
([1], '', '', '1'),
|
||||||
([-1], '', '', '-1'),
|
([-1], '', '', '-1'),
|
||||||
([0], '', '', '0'),
|
([0], '', '', '0'),
|
||||||
@@ -29,31 +33,32 @@ import pytest
|
|||||||
(['a'], '', '', 'a'),
|
(['a'], '', '', 'a'),
|
||||||
([1], 'and', '', '1'),
|
([1], 'and', '', '1'),
|
||||||
(['a'], 'and', '', 'a'),
|
(['a'], 'and', '', 'a'),
|
||||||
([1], 'and', '\'', '\'1\''),
|
([1], 'and', "'", "'1'"),
|
||||||
(['a'], 'and', '\'', '\'a\''),
|
(['a'], 'and', "'", "'a'"),
|
||||||
([1, 2], 'and', '', '1 and 2'),
|
([1, 2], 'and', '', '1 and 2'),
|
||||||
([1, 2], 'and', '\'', '\'1\' and \'2\''),
|
([1, 2], 'and', "'", "'1' and '2'"),
|
||||||
([1, 'a'], 'and', '', '1 and a'),
|
([1, 'a'], 'and', '', '1 and a'),
|
||||||
([1, 'a'], 'and', '\'', '\'1\' and \'a\''),
|
([1, 'a'], 'and', "'", "'1' and 'a'"),
|
||||||
([1, 'a'], 'and', '', '1 and a'),
|
([1, 'a'], 'and', '', '1 and a'),
|
||||||
([1, 'a'], 'and', '\'', '\'1\' and \'a\''),
|
([1, 'a'], 'and', "'", "'1' and 'a'"),
|
||||||
([1, 'a'], 'and', '\'', '\'1\' and \'a\''),
|
([1, 'a'], 'and', "'", "'1' and 'a'"),
|
||||||
([1, 'a'], '', '', '1 a'),
|
([1, 'a'], '', '', '1 a'),
|
||||||
([1, 'a'], '', '\'', '\'1\' \'a\''),
|
([1, 'a'], '', "'", "'1' 'a'"),
|
||||||
([1, 'a'], '', '', '1 a'),
|
([1, 'a'], '', '', '1 a'),
|
||||||
([1, 'a'], '', '\'', '\'1\' \'a\''),
|
([1, 'a'], '', "'", "'1' 'a'"),
|
||||||
([1, 'a'], '', '\'', '\'1\' \'a\''),
|
([1, 'a'], '', "'", "'1' 'a'"),
|
||||||
([1, 2, 3], 'and', '', '1, 2 and 3'),
|
([1, 2, 3], 'and', '', '1, 2 and 3'),
|
||||||
([1, 2, 3], '', '', '1, 2 3'),
|
([1, 2, 3], '', '', '1, 2 3'),
|
||||||
([1, 2, 3], 'and', '\'', '\'1\', \'2\' and \'3\''),
|
([1, 2, 3], 'and', "'", "'1', '2' and '3'"),
|
||||||
([1, 2, 3], '', '\'', '\'1\', \'2\' \'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],
|
[1, 2, 3, 42, 'a', 23],
|
||||||
'and',
|
'and',
|
||||||
'\'',
|
"'",
|
||||||
'\'1\', \'2\', \'3\', \'42\', \'a\' and \'23\'',
|
"'1', '2', '3', '42', 'a' and '23'",
|
||||||
),
|
),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_human_readable_list(ls, final_separator, quotation_mark, expected):
|
def test_human_readable_list(ls, final_separator, quotation_mark, expected):
|
||||||
assert human_readable_list(ls, final_separator, quotation_mark) == expected
|
assert human_readable_list(ls, final_separator, quotation_mark) == expected
|
||||||
@@ -12,10 +12,14 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from src.jcloud_docsgen.utils import non_empty_str
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.parametrize('value,expected', [
|
from src.jcloud_docsgen.utils import non_empty_str
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'value,expected',
|
||||||
|
[
|
||||||
(None, None),
|
(None, None),
|
||||||
('', None),
|
('', None),
|
||||||
(' ', None),
|
(' ', None),
|
||||||
@@ -25,6 +29,7 @@ import pytest
|
|||||||
(' a', ' a'),
|
(' a', ' a'),
|
||||||
('a ', 'a '),
|
('a ', 'a '),
|
||||||
('a', 'a'),
|
('a', 'a'),
|
||||||
])
|
],
|
||||||
|
)
|
||||||
def test_non_empty_str(value, expected):
|
def test_non_empty_str(value, expected):
|
||||||
assert non_empty_str(value) == expected
|
assert non_empty_str(value) == expected
|
||||||
@@ -12,23 +12,21 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from typing import Union
|
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
__all__ = [
|
__all__ = ['ast_node']
|
||||||
'ast_node'
|
|
||||||
]
|
|
||||||
|
|
||||||
def ast_node(statement: str) -> Union[ast.stmt, None]:
|
|
||||||
'''
|
def ast_node(statement: str) -> ast.stmt | None:
|
||||||
|
"""
|
||||||
Returns an AST node from the statement.
|
Returns an AST node from the statement.
|
||||||
|
|
||||||
:param statement: The statement.
|
:param statement: The statement.
|
||||||
:type statement: str
|
:type statement: str
|
||||||
|
|
||||||
:return: The node.
|
:return: The node.
|
||||||
:rtype: Union[ast.stmt, None]
|
:rtype: ast.stmt | None
|
||||||
'''
|
"""
|
||||||
|
|
||||||
body = ast.parse(statement).body
|
body = ast.parse(statement).body
|
||||||
if not body:
|
if not body:
|
||||||
|
|||||||
Reference in new issue
Block a user