From 907605c579a6b3ec43dc12e84a48c961a9e03429 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 3 Apr 2023 19:06:25 +0200 Subject: [PATCH 1/6] feat: parse docstrings in the `epydoc` format --- poetry.lock | 16 +- pyproject.toml | 1 + src/library_analyzer/cli/_cli.py | 28 +- src/library_analyzer/cli/_run_all.py | 5 +- src/library_analyzer/cli/_run_api.py | 5 +- .../processing/api/_ast_visitor.py | 16 +- .../processing/api/_get_api.py | 12 +- .../processing/api/_get_parameter_list.py | 8 +- .../api/docstring_parsing/__init__.py | 20 ++ .../_abstract_documentation_parser.py | 2 +- .../_create_docstring_parser.py | 13 + .../api/docstring_parsing/_docstring_style.py | 20 ++ .../api/docstring_parsing/_epydoc_parser.py | 100 +++++++ .../api/docstring_parsing/_helpers.py | 35 +++ .../_numpydoc_parser.py | 8 +- .../_plaintext_docstring_parser.py} | 10 +- .../api/documentation_parsing/__init__.py | 13 - .../_get_full_docstring.py | 15 -- .../processing/api/model/_api.py | 8 +- .../processing/api/model/_documentation.py | 2 - .../__init__.py | 0 .../docstring_parsing/test_epydoc_parser.py | 250 ++++++++++++++++++ .../test_get_full_docstring.py | 2 +- .../test_numpydoc_parser.py | 8 +- .../test_plaintext_docstring_parser.py} | 30 +-- .../processing/api/model/test_api.py | 3 +- .../api/model/test_documentation.py | 4 +- .../processing/api/test_get_parameter_list.py | 8 +- .../processing/migration/model/test_differ.py | 6 +- .../model/test_inheritance_differ.py | 10 +- .../migration/model/test_mapping.py | 14 +- .../migration/model/test_strict_differ.py | 6 +- .../migration/model/test_unchanged_differ.py | 6 +- .../annotations/called_after_migration.py | 54 ++-- .../annotations/description_migration.py | 18 +- .../migration/annotations/expert_migration.py | 16 +- .../migration/annotations/group_migration.py | 28 +- tests/migration/annotations/move_migration.py | 20 +- .../migration/annotations/remove_migration.py | 18 +- .../migration/annotations/rename_migration.py | 2 +- tests/migration/annotations/todo_migration.py | 2 +- tests/migration/test_migration.py | 8 +- 42 files changed, 642 insertions(+), 208 deletions(-) create mode 100644 src/library_analyzer/processing/api/docstring_parsing/__init__.py rename src/library_analyzer/processing/api/{documentation_parsing => docstring_parsing}/_abstract_documentation_parser.py (95%) create mode 100644 src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py create mode 100644 src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py create mode 100644 src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py create mode 100644 src/library_analyzer/processing/api/docstring_parsing/_helpers.py rename src/library_analyzer/processing/api/{documentation_parsing => docstring_parsing}/_numpydoc_parser.py (95%) rename src/library_analyzer/processing/api/{documentation_parsing/_default_documentation_parser.py => docstring_parsing/_plaintext_docstring_parser.py} (74%) delete mode 100644 src/library_analyzer/processing/api/documentation_parsing/__init__.py delete mode 100644 src/library_analyzer/processing/api/documentation_parsing/_get_full_docstring.py rename tests/library_analyzer/processing/api/{documentation_parsing => docstring_parsing}/__init__.py (100%) create mode 100644 tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py rename tests/library_analyzer/processing/api/{documentation_parsing => docstring_parsing}/test_get_full_docstring.py (94%) rename tests/library_analyzer/processing/api/{documentation_parsing => docstring_parsing}/test_numpydoc_parser.py (95%) rename tests/library_analyzer/processing/api/{documentation_parsing/test_default_documentation_parser.py => docstring_parsing/test_plaintext_docstring_parser.py} (73%) diff --git a/poetry.lock b/poetry.lock index ea411cce..32efb981 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -730,6 +730,18 @@ files = [ {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] +[[package]] +name = "docstring-parser" +version = "0.15" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "docstring_parser-0.15-py3-none-any.whl", hash = "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9"}, + {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, +] + [[package]] name = "docutils" version = "0.19" @@ -4012,4 +4024,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10,<3.12" -content-hash = "48a40db306ef188cee412f3d5d35ea0bc81a4d762b6d5767609830c7f96688b1" +content-hash = "21df624b7876d0d2644a04138658818b2b5732a9cb1f607b297e1a92ab80528c" diff --git a/pyproject.toml b/pyproject.toml index a690e529..d8d81e90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ levenshtein = "^0.20.9" numpydoc = "^1.5" scipy = "^1.10.1" spacy = "^3.5.1" +docstring-parser = "^0.15" [tool.poetry.group.dev.dependencies] pytest = "^7.2.1" diff --git a/src/library_analyzer/cli/_cli.py b/src/library_analyzer/cli/_cli.py index 0cbc162e..d26e033d 100644 --- a/src/library_analyzer/cli/_cli.py +++ b/src/library_analyzer/cli/_cli.py @@ -10,6 +10,7 @@ from library_analyzer.cli._run_api import _run_api_command from library_analyzer.cli._run_migrate import _run_migrate_command from library_analyzer.cli._run_usages import _run_usages_command +from library_analyzer.processing.api.docstring_parsing import DocstringStyle _API_COMMAND = "api" _USAGES_COMMAND = "usages" @@ -24,7 +25,7 @@ def cli() -> None: logging.basicConfig(level=logging.INFO) if args.command == _API_COMMAND: - _run_api_command(args.package, args.src, args.out) + _run_api_command(args.package, args.src, args.out, args.docstyle) elif args.command == _USAGES_COMMAND: _run_usages_command(args.package, args.client, args.out, args.processes, args.batchsize) elif args.command == _ANNOTATIONS_COMMAND: @@ -35,6 +36,7 @@ def cli() -> None: args.src, args.client, args.out, + args.docstyle, args.processes, args.batchsize, ) @@ -70,12 +72,20 @@ def _add_api_subparser(subparsers: _SubParsersAction) -> None: "-s", "--src", help="Directory containing the Python code of the package. If this is omitted, we try to locate the package " - "with the given name in the current Python interpreter.", + "with the given name in the current Python interpreter.", type=Path, required=False, default=None, ) api_parser.add_argument("-o", "--out", help="Output directory.", type=Path, required=True) + api_parser.add_argument( + "--docstyle", + help="The docstring style.", + type=DocstringStyle.from_string, + choices=list(DocstringStyle), + required=False, + default=DocstringStyle.PLAINTEXT.name, + ), def _add_usages_subparser(subparsers: _SubParsersAction) -> None: @@ -146,7 +156,7 @@ def _add_all_subparser(subparsers: _SubParsersAction) -> None: "-s", "--src", help="Directory containing the Python code of the package. If this is omitted, we try to locate the package " - "with the given name in the current Python interpreter.", + "with the given name in the current Python interpreter.", type=Path, required=False, default=None, @@ -159,20 +169,28 @@ def _add_all_subparser(subparsers: _SubParsersAction) -> None: required=True, ) all_parser.add_argument("-o", "--out", help="Output directory.", type=Path, required=True) + all_parser.add_argument( + "--docstyle", + help="The docstring style.", + type=DocstringStyle.from_string, + choices=list(DocstringStyle), + required=False, + default=DocstringStyle.PLAINTEXT.name, + ), all_parser.add_argument( "--processes", help="How many processes should be spawned during processing.", type=int, required=False, default=4, - ) + ), all_parser.add_argument( "--batchsize", help="How many files to process in one go. Higher values lead to higher memory usage but better performance.", type=int, required=False, default=100, - ) + ), def _add_migrate_subparser(subparsers: _SubParsersAction) -> None: diff --git a/src/library_analyzer/cli/_run_all.py b/src/library_analyzer/cli/_run_all.py index dd4eae70..a2c24ed4 100644 --- a/src/library_analyzer/cli/_run_all.py +++ b/src/library_analyzer/cli/_run_all.py @@ -7,6 +7,8 @@ from library_analyzer.cli._run_api import _run_api_command from library_analyzer.cli._run_usages import _run_usages_command from library_analyzer.cli._shared_constants import _API_KEY, _USAGES_KEY +from library_analyzer.processing.api.docstring_parsing import DocstringStyle + def _run_all_command( @@ -14,12 +16,13 @@ def _run_all_command( src_dir_path: Path, client_dir_path: Path, out_dir_path: Path, + docstring_style: DocstringStyle, n_processes: int, batch_size: int, ) -> None: out_file_annotations = out_dir_path.joinpath("annotations.json") results = _run_in_parallel( - partial(_run_api_command, package, src_dir_path, out_dir_path), + partial(_run_api_command, package, src_dir_path, out_dir_path, docstring_style), partial( _run_usages_command, package, diff --git a/src/library_analyzer/cli/_run_api.py b/src/library_analyzer/cli/_run_api.py index d0eb6a21..547fd72c 100644 --- a/src/library_analyzer/cli/_run_api.py +++ b/src/library_analyzer/cli/_run_api.py @@ -2,6 +2,8 @@ from library_analyzer.processing.api import get_api from library_analyzer.processing.dependencies import get_dependencies +from library_analyzer.processing.api.docstring_parsing import DocstringStyle + from ._read_and_write_file import _write_api_dependency_file, _write_api_file from ._shared_constants import _API_KEY @@ -11,9 +13,10 @@ def _run_api_command( package: str, src_dir_path: Path, out_dir_path: Path, + docstring_style: DocstringStyle, result_dict: dict | None = None, ) -> None: - api = get_api(package, src_dir_path) + api = get_api(package, src_dir_path, docstring_style) api_dependencies = get_dependencies(api) api_file_path = _write_api_file(api, out_dir_path) diff --git a/src/library_analyzer/processing/api/_ast_visitor.py b/src/library_analyzer/processing/api/_ast_visitor.py index 3beac7de..12b99805 100644 --- a/src/library_analyzer/processing/api/_ast_visitor.py +++ b/src/library_analyzer/processing/api/_ast_visitor.py @@ -19,7 +19,7 @@ from ._file_filters import _is_init_file from ._get_instance_attributes import get_instance_attributes from ._get_parameter_list import get_parameter_list -from .documentation_parsing import AbstractDocumentationParser +from .docstring_parsing import AbstractDocstringParser def trim_code(code: str | None, from_line_no: int, to_line_no: int, encoding: str) -> str: @@ -32,8 +32,12 @@ def trim_code(code: str | None, from_line_no: int, to_line_no: int, encoding: st class _AstVisitor: - def __init__(self, documentation_parser: AbstractDocumentationParser, api: API) -> None: - self.documentation_parser: AbstractDocumentationParser = documentation_parser + def __init__( + self, + docstring_parser: AbstractDocstringParser, + api: API + ) -> None: + self.docstring_parser: AbstractDocstringParser = docstring_parser self.reexported: dict[str, list[str]] = {} self.api: API = api self.__declaration_stack: list[Module | Class | Function] = [] @@ -150,7 +154,7 @@ def enter_classdef(self, class_node: astroid.ClassDef) -> None: superclasses=class_node.basenames, is_public=self.is_public(class_node.name, qname), reexported_by=self.reexported.get(qname, []), - documentation=self.documentation_parser.get_class_documentation(class_node), + documentation=self.docstring_parser.get_class_documentation(class_node), code=code, instance_attributes=instance_attributes, ) @@ -188,7 +192,7 @@ def enter_functiondef(self, function_node: astroid.FunctionDef) -> None: qname=qname, decorators=decorator_names, parameters=get_parameter_list( - self.documentation_parser, + self.docstring_parser, function_node, function_id, qname, @@ -197,7 +201,7 @@ def enter_functiondef(self, function_node: astroid.FunctionDef) -> None: results=[], # TODO: results is_public=is_public, reexported_by=self.reexported.get(qname, []), - documentation=self.documentation_parser.get_function_documentation(function_node), + documentation=self.docstring_parser.get_function_documentation(function_node), code=code, ) self.__declaration_stack.append(function) diff --git a/src/library_analyzer/processing/api/_get_api.py b/src/library_analyzer/processing/api/_get_api.py index 1d409baa..762e9d05 100644 --- a/src/library_analyzer/processing/api/_get_api.py +++ b/src/library_analyzer/processing/api/_get_api.py @@ -14,10 +14,14 @@ package_files, package_root, ) -from .documentation_parsing import NumpyDocParser +from .docstring_parsing import DocstringStyle, create_docstring_parser -def get_api(package_name: str, root: Path | None = None) -> API: +def get_api( + package_name: str, + root: Path | None = None, + docstring_style: DocstringStyle = DocstringStyle.PLAINTEXT, +) -> API: if root is None: root = package_root(package_name) dist = distribution(package_name) or "" @@ -25,8 +29,8 @@ def get_api(package_name: str, root: Path | None = None) -> API: files = package_files(root) api = API(dist, package_name, dist_version) - documentation_parser = NumpyDocParser() - callable_visitor = _AstVisitor(documentation_parser, api) + docstring_parser = create_docstring_parser(docstring_style) + callable_visitor = _AstVisitor(docstring_parser, api) walker = ASTWalker(callable_visitor) for file in files: diff --git a/src/library_analyzer/processing/api/_get_parameter_list.py b/src/library_analyzer/processing/api/_get_parameter_list.py index b038167c..9e908d74 100644 --- a/src/library_analyzer/processing/api/_get_parameter_list.py +++ b/src/library_analyzer/processing/api/_get_parameter_list.py @@ -1,13 +1,13 @@ import astroid -from library_analyzer.processing.api.documentation_parsing import ( - AbstractDocumentationParser, +from library_analyzer.processing.api.docstring_parsing import ( + AbstractDocstringParser, ) from library_analyzer.processing.api.model import Parameter, ParameterAssignment def get_parameter_list( - documentation_parser: AbstractDocumentationParser, + docstring_parser: AbstractDocstringParser, function_node: astroid.FunctionDef, function_id: str, function_qname: str, @@ -27,7 +27,7 @@ def get_parameter_list( default_value=_get_stringified_default_value(function_node, parameter_name), assigned_by=parameter_assigned_by, is_public=function_is_public, - documentation=documentation_parser.get_parameter_documentation( + documentation=docstring_parser.get_parameter_documentation( function_node, parameter_name, parameter_assigned_by, diff --git a/src/library_analyzer/processing/api/docstring_parsing/__init__.py b/src/library_analyzer/processing/api/docstring_parsing/__init__.py new file mode 100644 index 00000000..257ea18c --- /dev/null +++ b/src/library_analyzer/processing/api/docstring_parsing/__init__.py @@ -0,0 +1,20 @@ +"""Parsing docstrings into a common format.""" + +from ._abstract_documentation_parser import AbstractDocstringParser +from ._create_docstring_parser import create_docstring_parser +from ._docstring_style import DocstringStyle +from ._epydoc_parser import EpydocParser +from ._helpers import get_description, get_full_docstring +from ._numpydoc_parser import NumpyDocParser +from ._plaintext_docstring_parser import PlaintextDocstringParser + +__all__ = [ + "AbstractDocstringParser", + "create_docstring_parser", + "DocstringStyle", + "EpydocParser", + "get_description", + "get_full_docstring", + "NumpyDocParser", + "PlaintextDocstringParser", +] diff --git a/src/library_analyzer/processing/api/documentation_parsing/_abstract_documentation_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_abstract_documentation_parser.py similarity index 95% rename from src/library_analyzer/processing/api/documentation_parsing/_abstract_documentation_parser.py rename to src/library_analyzer/processing/api/docstring_parsing/_abstract_documentation_parser.py index 4cdd2fc6..5e9fbc4e 100644 --- a/src/library_analyzer/processing/api/documentation_parsing/_abstract_documentation_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_abstract_documentation_parser.py @@ -14,7 +14,7 @@ ) -class AbstractDocumentationParser(ABC): +class AbstractDocstringParser(ABC): @abstractmethod def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocumentation: pass diff --git a/src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py new file mode 100644 index 00000000..a400fe78 --- /dev/null +++ b/src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py @@ -0,0 +1,13 @@ +from ._numpydoc_parser import NumpyDocParser +from ._plaintext_docstring_parser import PlaintextDocstringParser +from ._docstring_style import DocstringStyle +from ._epydoc_parser import EpydocParser + + +def create_docstring_parser(style: DocstringStyle): + if style == DocstringStyle.NUMPY: + return NumpyDocParser() + if style == DocstringStyle.EPYDOC: + return EpydocParser() + else: # TODO: cover other cases + return PlaintextDocstringParser() diff --git a/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py new file mode 100644 index 00000000..c4c6ca69 --- /dev/null +++ b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py @@ -0,0 +1,20 @@ +from enum import Enum + + +class DocstringStyle(Enum): + # AUTO = "auto", + PLAINTEXT = "plaintext", + # REST = "reST", + NUMPY = "numpy", + # GOOGLE = "google", + EPYDOC = "epydoc" + + def __str__(self): + return self.name + + @staticmethod + def from_string(key: str): + try: + return DocstringStyle[key.upper()] + except KeyError: + raise ValueError(f"Unknown docstring style: {key}") diff --git a/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py new file mode 100644 index 00000000..42d71581 --- /dev/null +++ b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py @@ -0,0 +1,100 @@ +from typing import Optional + +import astroid +from docstring_parser import parse as parse_docstring, DocstringStyle, Docstring, DocstringParam + +from library_analyzer.processing.api.model import ( + ClassDocumentation, + FunctionDocumentation, + ParameterAssignment, + ParameterDocumentation, +) +from ._abstract_documentation_parser import AbstractDocstringParser +from ._helpers import get_full_docstring, get_description + + +class EpydocParser(AbstractDocstringParser): + """ + Parses documentation in the Epydoc format. See http://epydoc.sourceforge.net/epytext.html for more information. + + This class is not thread-safe. Each thread should create its own instance. + """ + + def __init__(self): + self.__cached_function_node: Optional[astroid.FunctionDef] = None + self.__cached_docstring: Optional[DocstringParam] = None + + def get_class_documentation( + self, class_node: astroid.ClassDef + ) -> ClassDocumentation: + docstring = get_full_docstring(class_node) + docstring_obj = parse_docstring(docstring, style=DocstringStyle.EPYDOC) + + return ClassDocumentation( + description=get_description(docstring_obj) + ) + + def get_function_documentation( + self, function_node: astroid.FunctionDef + ) -> FunctionDocumentation: + docstring = get_full_docstring(function_node) + + return FunctionDocumentation( + description=get_description( + self.__get_cached_function_numpydoc_string(function_node, docstring) + ) + ) + + def get_parameter_documentation( + self, + function_node: astroid.FunctionDef, + parameter_name: str, + parameter_assigned_by: ParameterAssignment, + ) -> ParameterDocumentation: + + # For constructors (__init__ functions) the parameters are described on the class + if function_node.name == "__init__" and isinstance( + function_node.parent, astroid.ClassDef + ): + docstring = get_full_docstring(function_node.parent) + else: + docstring = get_full_docstring(function_node) + + # Find matching parameter docstrings + function_numpydoc = self.__get_cached_function_numpydoc_string( + function_node, docstring + ) + all_parameters_numpydoc: list[DocstringParam] = function_numpydoc.params + matching_parameters_numpydoc = [ + it + for it in all_parameters_numpydoc + if it.arg_name == parameter_name + ] + + if len(matching_parameters_numpydoc) == 0: + return ParameterDocumentation(type="", default_value="", description="") + + last_parameter_docstring_obj = matching_parameters_numpydoc[-1] + return ParameterDocumentation( + type=last_parameter_docstring_obj.type_name or "", + default_value=last_parameter_docstring_obj.default or "", + description=last_parameter_docstring_obj.description, + ) + + def __get_cached_function_numpydoc_string( + self, function_node: astroid.FunctionDef, docstring: str + ) -> Docstring: + """ + Returns the NumpyDocString for the given function node. It is only recomputed when the function node differs + from the previous one that was passed to this function. This avoids reparsing the docstring for the function + itself and all of its parameters. + + On Lars's system this caused a significant performance improvement: Previously, 8.382s were spent inside the + function get_parameter_documentation when parsing sklearn. Afterwards, it was only 2.113s. + """ + + if self.__cached_function_node is not function_node: + self.__cached_function_node = function_node + self.__cached_docstring = parse_docstring(docstring, style=DocstringStyle.EPYDOC) + + return self.__cached_docstring diff --git a/src/library_analyzer/processing/api/docstring_parsing/_helpers.py b/src/library_analyzer/processing/api/docstring_parsing/_helpers.py new file mode 100644 index 00000000..5bcebe12 --- /dev/null +++ b/src/library_analyzer/processing/api/docstring_parsing/_helpers.py @@ -0,0 +1,35 @@ +import inspect +from typing import Union + +import astroid +from docstring_parser import Docstring + + +def get_full_docstring( + declaration: Union[astroid.ClassDef, astroid.FunctionDef] +) -> str: + """ + Returns the full docstring of the given declaration or an empty string if no docstring is available. Indentation is + cleaned up. + """ + + doc_node = declaration.doc_node + if doc_node is None: + return "" + return inspect.cleandoc(doc_node.value) + + +def get_description(docstring_obj: Docstring) -> str: + """ + Returns the concatenated short and long description of the given docstring object or an empty string if these parts + are blank. + """ + + summary: str = docstring_obj.short_description or "" + extended_summary: str = docstring_obj.long_description or "" + + result = "" + result += summary.rstrip() + result += "\n\n" + result += extended_summary.rstrip() + return result.strip() diff --git a/src/library_analyzer/processing/api/documentation_parsing/_numpydoc_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py similarity index 95% rename from src/library_analyzer/processing/api/documentation_parsing/_numpydoc_parser.py rename to src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py index 6057e789..5e23b0a7 100644 --- a/src/library_analyzer/processing/api/documentation_parsing/_numpydoc_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py @@ -11,11 +11,11 @@ ParameterDocumentation, ) -from ._abstract_documentation_parser import AbstractDocumentationParser -from ._get_full_docstring import get_full_docstring +from ._abstract_documentation_parser import AbstractDocstringParser +from ._helpers import get_full_docstring -class NumpyDocParser(AbstractDocumentationParser): +class NumpyDocParser(AbstractDocstringParser): """ Parses documentation in the NumpyDoc format. @@ -37,7 +37,6 @@ def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocument return ClassDocumentation( description=_get_description(NumpyDocString(docstring)), - full_docstring=docstring, ) def get_function_documentation(self, function_node: astroid.FunctionDef) -> FunctionDocumentation: @@ -45,7 +44,6 @@ def get_function_documentation(self, function_node: astroid.FunctionDef) -> Func return FunctionDocumentation( description=_get_description(self.__get_cached_function_numpydoc_string(function_node, docstring)), - full_docstring=docstring, ) def get_parameter_documentation( diff --git a/src/library_analyzer/processing/api/documentation_parsing/_default_documentation_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_plaintext_docstring_parser.py similarity index 74% rename from src/library_analyzer/processing/api/documentation_parsing/_default_documentation_parser.py rename to src/library_analyzer/processing/api/docstring_parsing/_plaintext_docstring_parser.py index b8e30d40..fdbece72 100644 --- a/src/library_analyzer/processing/api/documentation_parsing/_default_documentation_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_plaintext_docstring_parser.py @@ -7,21 +7,21 @@ ParameterDocumentation, ) -from ._abstract_documentation_parser import AbstractDocumentationParser -from ._get_full_docstring import get_full_docstring +from ._abstract_documentation_parser import AbstractDocstringParser +from ._helpers import get_full_docstring -class DefaultDocumentationParser(AbstractDocumentationParser): +class PlaintextDocstringParser(AbstractDocstringParser): """Parses documentation in any format. Should not be used if there is another parser for the specific format.""" def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocumentation: return ClassDocumentation( - full_docstring=get_full_docstring(class_node), + description=get_full_docstring(class_node), ) def get_function_documentation(self, function_node: astroid.FunctionDef) -> FunctionDocumentation: return FunctionDocumentation( - full_docstring=get_full_docstring(function_node), + description=get_full_docstring(function_node), ) def get_parameter_documentation( diff --git a/src/library_analyzer/processing/api/documentation_parsing/__init__.py b/src/library_analyzer/processing/api/documentation_parsing/__init__.py deleted file mode 100644 index fc1c5edf..00000000 --- a/src/library_analyzer/processing/api/documentation_parsing/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Parsing docstrings into a common format.""" - -from ._abstract_documentation_parser import AbstractDocumentationParser -from ._default_documentation_parser import DefaultDocumentationParser -from ._get_full_docstring import get_full_docstring -from ._numpydoc_parser import NumpyDocParser - -__all__ = [ - "AbstractDocumentationParser", - "DefaultDocumentationParser", - "NumpyDocParser", - "get_full_docstring", -] diff --git a/src/library_analyzer/processing/api/documentation_parsing/_get_full_docstring.py b/src/library_analyzer/processing/api/documentation_parsing/_get_full_docstring.py deleted file mode 100644 index 5db4f557..00000000 --- a/src/library_analyzer/processing/api/documentation_parsing/_get_full_docstring.py +++ /dev/null @@ -1,15 +0,0 @@ -import inspect - -import astroid - - -def get_full_docstring(declaration: astroid.ClassDef | astroid.FunctionDef) -> str: - """ - Return the full docstring of the given declaration. - - Indentation is cleaned up. If no docstring is available, an empty string is returned. - """ - doc_node = declaration.doc_node - if doc_node is None: - return "" - return inspect.cleandoc(doc_node.value) diff --git a/src/library_analyzer/processing/api/model/_api.py b/src/library_analyzer/processing/api/model/_api.py index 70aa19e5..17d008c3 100644 --- a/src/library_analyzer/processing/api/model/_api.py +++ b/src/library_analyzer/processing/api/model/_api.py @@ -235,8 +235,7 @@ def from_json(json: Any) -> Class: json.get("is_public", True), json.get("reexported_by", []), ClassDocumentation( - description=json.get("description", ""), - full_docstring=json.get("docstring", ""), + description=json.get("description", "") ), json.get("code", ""), [ @@ -271,7 +270,6 @@ def to_json(self) -> Any: "is_public": self.is_public, "reexported_by": self.reexported_by, "description": self.documentation.description, - "docstring": self.documentation.full_docstring, "code": self.code, "instance_attributes": [attribute.to_json() for attribute in self.instance_attributes], } @@ -361,8 +359,7 @@ def from_json(json: Any) -> Function: json.get("is_public", True), json.get("reexported_by", []), FunctionDocumentation( - description=json.get("description", ""), - full_docstring=json.get("docstring", ""), + description=json.get("description", "") ), json.get("code", ""), ) @@ -382,7 +379,6 @@ def to_json(self) -> Any: "is_public": self.is_public, "reexported_by": self.reexported_by, "description": self.documentation.description, - "docstring": self.documentation.full_docstring, "code": self.code, } diff --git a/src/library_analyzer/processing/api/model/_documentation.py b/src/library_analyzer/processing/api/model/_documentation.py index ccbae18d..7909b4f3 100644 --- a/src/library_analyzer/processing/api/model/_documentation.py +++ b/src/library_analyzer/processing/api/model/_documentation.py @@ -7,7 +7,6 @@ @dataclass(frozen=True) class ClassDocumentation: description: str = "" - full_docstring: str = "" @staticmethod def from_dict(d: dict) -> ClassDocumentation: @@ -20,7 +19,6 @@ def to_dict(self) -> dict: @dataclass(frozen=True) class FunctionDocumentation: description: str = "" - full_docstring: str = "" @staticmethod def from_dict(d: dict) -> FunctionDocumentation: diff --git a/tests/library_analyzer/processing/api/documentation_parsing/__init__.py b/tests/library_analyzer/processing/api/docstring_parsing/__init__.py similarity index 100% rename from tests/library_analyzer/processing/api/documentation_parsing/__init__.py rename to tests/library_analyzer/processing/api/docstring_parsing/__init__.py diff --git a/tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py b/tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py new file mode 100644 index 00000000..5608db24 --- /dev/null +++ b/tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py @@ -0,0 +1,250 @@ +import astroid +import pytest +from library_analyzer.processing.api.docstring_parsing import EpydocParser +from library_analyzer.processing.api.model import ( + ClassDocumentation, + FunctionDocumentation, + ParameterAssignment, + ParameterDocumentation, +) + + +@pytest.fixture +def epydoc_parser() -> EpydocParser: + return EpydocParser() + + +# language=python +class_with_documentation = ''' +class C: + """ + Lorem ipsum. Code:: + + pass + + Dolor sit amet. + """ +''' + +# language=python +class_without_documentation = """ +class C: + pass +""" + + +@pytest.mark.parametrize( + "python_code, expected_class_documentation", + [ + ( + class_with_documentation, + ClassDocumentation( + description="Lorem ipsum. Code::\n\npass\n\nDolor sit amet.", + ), + ), + ( + class_without_documentation, + ClassDocumentation(description=""), + ), + ], + ids=[ + "class with documentation", + "class without documentation", + ], +) +def test_get_class_documentation( + epydoc_parser: EpydocParser, + python_code: str, + expected_class_documentation: ClassDocumentation, +): + node = astroid.extract_node(python_code) + + assert isinstance(node, astroid.ClassDef) + assert epydoc_parser.get_class_documentation(node) == expected_class_documentation + + +# language=python +function_with_documentation = ''' +def f(): + """ + Lorem ipsum. Code:: + + pass + + Dolor sit amet. + """ + + pass +''' + +# language=python +function_without_documentation = """ +def f(): + pass +""" + + +@pytest.mark.parametrize( + "python_code, expected_function_documentation", + [ + ( + function_with_documentation, + FunctionDocumentation( + description="Lorem ipsum. Code::\n\npass\n\nDolor sit amet." + ), + ), + ( + function_without_documentation, + FunctionDocumentation(description=""), + ), + ], + ids=[ + "function with documentation", + "function without documentation", + ], +) +def test_get_function_documentation( + epydoc_parser: EpydocParser, + python_code: str, + expected_function_documentation: FunctionDocumentation, +): + node = astroid.extract_node(python_code) + + assert isinstance(node, astroid.FunctionDef) + assert ( + epydoc_parser.get_function_documentation(node) + == expected_function_documentation + ) + + +# language=python +class_with_parameters = ''' +# noinspection PyUnresolvedReferences,PyIncorrectDocstring +class C: + """ + Lorem ipsum. + + Dolor sit amet. + + @param p: foo defaults to 1 + @type p: int + """ + + def __init__(self): + pass +''' + +# language=python +function_with_parameters = ''' +# noinspection PyUnresolvedReferences,PyIncorrectDocstring +def f(): + """ + Lorem ipsum. + + Dolor sit amet. + + Parameters + ---------- + @param no_type_no_default: no type and no default + @param type_no_default: type but no default + @type type_no_default: int + @param with_default: foo that defaults to 2 + @type with_default: int + """ + + pass +''' + + +@pytest.mark.parametrize( + "python_code, parameter_name, parameter_assigned_by, expected_parameter_documentation", + [ + ( + class_with_parameters, + "p", + ParameterAssignment.POSITION_OR_NAME, + ParameterDocumentation( + type="int", + default_value="1", + description="foo defaults to 1", + ), + ), + ( + class_with_parameters, + "missing", + ParameterAssignment.POSITION_OR_NAME, + ParameterDocumentation( + type="", + default_value="", + description="", + ), + ), + ( + function_with_parameters, + "no_type_no_default", + ParameterAssignment.POSITION_OR_NAME, + ParameterDocumentation( + type="", + default_value="", + description="no type and no default", + ), + ), + ( + function_with_parameters, + "type_no_default", + ParameterAssignment.POSITION_OR_NAME, + ParameterDocumentation( + type="int", + default_value="", + description="type but no default", + ), + ), + ( + function_with_parameters, + "with_default", + ParameterAssignment.POSITION_OR_NAME, + ParameterDocumentation( + type="int", + default_value="2", + description="foo that defaults to 2", + ), + ), + ( + function_with_parameters, + "missing", + ParameterAssignment.POSITION_OR_NAME, + ParameterDocumentation(type="", default_value="", description=""), + ), + ], + ids=[ + "existing class parameter", + "missing class parameter", + "function parameter with no type and no default", + "function parameter with type and no default", + "function parameter with default", + "missing function parameter", + ], +) +def test_get_parameter_documentation( + epydoc_parser: EpydocParser, + python_code: str, + parameter_name: str, + parameter_assigned_by: ParameterAssignment, + expected_parameter_documentation: ParameterDocumentation, +): + node = astroid.extract_node(python_code) + assert isinstance(node, astroid.ClassDef) or isinstance(node, astroid.FunctionDef) + + # Find the constructor + if isinstance(node, astroid.ClassDef): + for method in node.mymethods(): + if method.name == "__init__": + node = method + + assert isinstance(node, astroid.FunctionDef) + assert ( + epydoc_parser.get_parameter_documentation( + node, parameter_name, parameter_assigned_by + ) + == expected_parameter_documentation + ) diff --git a/tests/library_analyzer/processing/api/documentation_parsing/test_get_full_docstring.py b/tests/library_analyzer/processing/api/docstring_parsing/test_get_full_docstring.py similarity index 94% rename from tests/library_analyzer/processing/api/documentation_parsing/test_get_full_docstring.py rename to tests/library_analyzer/processing/api/docstring_parsing/test_get_full_docstring.py index ff232c5b..f8155948 100644 --- a/tests/library_analyzer/processing/api/documentation_parsing/test_get_full_docstring.py +++ b/tests/library_analyzer/processing/api/docstring_parsing/test_get_full_docstring.py @@ -1,6 +1,6 @@ import astroid import pytest -from library_analyzer.processing.api.documentation_parsing import get_full_docstring +from library_analyzer.processing.api.docstring_parsing import get_full_docstring class_with_multi_line_documentation = ''' class C: diff --git a/tests/library_analyzer/processing/api/documentation_parsing/test_numpydoc_parser.py b/tests/library_analyzer/processing/api/docstring_parsing/test_numpydoc_parser.py similarity index 95% rename from tests/library_analyzer/processing/api/documentation_parsing/test_numpydoc_parser.py rename to tests/library_analyzer/processing/api/docstring_parsing/test_numpydoc_parser.py index 7b0fc52a..1e1cc85f 100644 --- a/tests/library_analyzer/processing/api/documentation_parsing/test_numpydoc_parser.py +++ b/tests/library_analyzer/processing/api/docstring_parsing/test_numpydoc_parser.py @@ -1,6 +1,6 @@ import astroid import pytest -from library_analyzer.processing.api.documentation_parsing import NumpyDocParser +from library_analyzer.processing.api.docstring_parsing import NumpyDocParser from library_analyzer.processing.api.model import ( ClassDocumentation, FunctionDocumentation, @@ -38,12 +38,11 @@ class C: class_with_documentation, ClassDocumentation( description="Lorem ipsum. Code::\n\n pass\n\nDolor sit amet.", - full_docstring="Lorem ipsum. Code::\n\n pass\n\nDolor sit amet.", ), ), ( class_without_documentation, - ClassDocumentation(description="", full_docstring=""), + ClassDocumentation(description=""), ), ], ids=[ @@ -88,12 +87,11 @@ def f(): function_with_documentation, FunctionDocumentation( description="Lorem ipsum. Code::\n\n pass\n\nDolor sit amet.", - full_docstring="Lorem ipsum. Code::\n\n pass\n\nDolor sit amet.", ), ), ( function_without_documentation, - FunctionDocumentation(description="", full_docstring=""), + FunctionDocumentation(description=""), ), ], ids=[ diff --git a/tests/library_analyzer/processing/api/documentation_parsing/test_default_documentation_parser.py b/tests/library_analyzer/processing/api/docstring_parsing/test_plaintext_docstring_parser.py similarity index 73% rename from tests/library_analyzer/processing/api/documentation_parsing/test_default_documentation_parser.py rename to tests/library_analyzer/processing/api/docstring_parsing/test_plaintext_docstring_parser.py index c38a07a1..10fc15de 100644 --- a/tests/library_analyzer/processing/api/documentation_parsing/test_default_documentation_parser.py +++ b/tests/library_analyzer/processing/api/docstring_parsing/test_plaintext_docstring_parser.py @@ -1,7 +1,7 @@ import astroid import pytest -from library_analyzer.processing.api.documentation_parsing import ( - DefaultDocumentationParser, +from library_analyzer.processing.api.docstring_parsing import ( + PlaintextDocstringParser, ) from library_analyzer.processing.api.model import ( ClassDocumentation, @@ -12,8 +12,8 @@ @pytest.fixture() -def default_documentation_parser() -> DefaultDocumentationParser: - return DefaultDocumentationParser() +def plaintext_docstring_parser() -> PlaintextDocstringParser: + return PlaintextDocstringParser() class_with_documentation = ''' @@ -40,13 +40,12 @@ class C: ( class_with_documentation, ClassDocumentation( - description="", - full_docstring="Lorem ipsum.\n\nDolor sit amet.", + description="Lorem ipsum.\n\nDolor sit amet.", ), ), ( class_without_documentation, - ClassDocumentation(description="", full_docstring=""), + ClassDocumentation(description=""), ), ], ids=[ @@ -55,14 +54,14 @@ class C: ], ) def test_get_class_documentation( - default_documentation_parser: DefaultDocumentationParser, + plaintext_docstring_parser: PlaintextDocstringParser, python_code: str, expected_class_documentation: ClassDocumentation, ) -> None: node = astroid.extract_node(python_code) assert isinstance(node, astroid.ClassDef) - assert default_documentation_parser.get_class_documentation(node) == expected_class_documentation + assert plaintext_docstring_parser.get_class_documentation(node) == expected_class_documentation function_with_documentation = ''' @@ -88,13 +87,12 @@ def f(p: int): ( function_with_documentation, FunctionDocumentation( - description="", - full_docstring="Lorem ipsum.\n\nDolor sit amet.", + description="Lorem ipsum.\n\nDolor sit amet.", ), ), ( function_without_documentation, - FunctionDocumentation(description="", full_docstring=""), + FunctionDocumentation(description=""), ), ], ids=[ @@ -103,14 +101,14 @@ def f(p: int): ], ) def test_get_function_documentation( - default_documentation_parser: DefaultDocumentationParser, + plaintext_docstring_parser: PlaintextDocstringParser, python_code: str, expected_function_documentation: FunctionDocumentation, ) -> None: node = astroid.extract_node(python_code) assert isinstance(node, astroid.FunctionDef) - assert default_documentation_parser.get_function_documentation(node) == expected_function_documentation + assert plaintext_docstring_parser.get_function_documentation(node) == expected_function_documentation @pytest.mark.parametrize( @@ -141,7 +139,7 @@ def test_get_function_documentation( ], ) def test_get_parameter_documentation( - default_documentation_parser: DefaultDocumentationParser, + plaintext_docstring_parser: PlaintextDocstringParser, python_code: str, parameter_name: str, expected_parameter_documentation: ParameterDocumentation, @@ -149,7 +147,7 @@ def test_get_parameter_documentation( node = astroid.extract_node(python_code) assert isinstance(node, astroid.FunctionDef) assert ( - default_documentation_parser.get_parameter_documentation( + plaintext_docstring_parser.get_parameter_documentation( node, parameter_name, ParameterAssignment.POSITION_OR_NAME, diff --git a/tests/library_analyzer/processing/api/model/test_api.py b/tests/library_analyzer/processing/api/model/test_api.py index a7b7808d..dceaa8cb 100644 --- a/tests/library_analyzer/processing/api/model/test_api.py +++ b/tests/library_analyzer/processing/api/model/test_api.py @@ -217,7 +217,6 @@ def test_cut_documentation_from_code(code: str, expected_code: str) -> None: reexported_by=[], documentation=ClassDocumentation( "this documentation string cannot be used", - " because indentation was removed", ), code=code, instance_attributes=[], @@ -231,7 +230,7 @@ def test_cut_documentation_from_code(code: str, expected_code: str) -> None: results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code=code, ) assert api_element.get_formatted_code(cut_documentation=True) == expected_code + "\n" diff --git a/tests/library_analyzer/processing/api/model/test_documentation.py b/tests/library_analyzer/processing/api/model/test_documentation.py index 12b5c596..19f8b9bf 100644 --- a/tests/library_analyzer/processing/api/model/test_documentation.py +++ b/tests/library_analyzer/processing/api/model/test_documentation.py @@ -10,7 +10,7 @@ "class_documentation", [ ClassDocumentation(), - ClassDocumentation(description="foo", full_docstring="foo bar"), + ClassDocumentation(description="foo"), ], ) def test_dict_conversion_for_class_documentation( @@ -23,7 +23,7 @@ def test_dict_conversion_for_class_documentation( "function_documentation", [ FunctionDocumentation(), - FunctionDocumentation(description="foo", full_docstring="foo bar"), + FunctionDocumentation(description="foo"), ], ) def test_dict_conversion_for_function_documentation( diff --git a/tests/library_analyzer/processing/api/test_get_parameter_list.py b/tests/library_analyzer/processing/api/test_get_parameter_list.py index 0d58fcac..e77b2716 100644 --- a/tests/library_analyzer/processing/api/test_get_parameter_list.py +++ b/tests/library_analyzer/processing/api/test_get_parameter_list.py @@ -1,8 +1,8 @@ import astroid import pytest from library_analyzer.processing.api import get_parameter_list -from library_analyzer.processing.api.documentation_parsing import ( - DefaultDocumentationParser, +from library_analyzer.processing.api.docstring_parsing import ( + PlaintextDocstringParser, ) from library_analyzer.processing.api.model import ( Parameter, @@ -119,7 +119,7 @@ def test_get_parameter_list_on_global_functions(python_code: str, expected_param actual_parameter_list = [ it.to_json() for it in get_parameter_list( - documentation_parser=DefaultDocumentationParser(), + docstring_parser=PlaintextDocstringParser(), function_node=node, function_id="f", function_qname="f", @@ -257,7 +257,7 @@ def test_get_parameter_list_on_method(python_code: str, expected_parameter_list: actual_parameter_list = [ it.to_json() for it in get_parameter_list( - documentation_parser=DefaultDocumentationParser(), + docstring_parser=PlaintextDocstringParser(), function_node=node, function_id="C/f", function_qname="C.f", diff --git a/tests/library_analyzer/processing/migration/model/test_differ.py b/tests/library_analyzer/processing/migration/model/test_differ.py index 2537c946..f4ce1c93 100644 --- a/tests/library_analyzer/processing/migration/model/test_differ.py +++ b/tests/library_analyzer/processing/migration/model/test_differ.py @@ -69,7 +69,7 @@ class Test: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code=code_a, instance_attributes=[], ) @@ -87,7 +87,7 @@ class newTest: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a new test", "This is a new test"), + documentation=ClassDocumentation("This is a new test"), code=code_b, instance_attributes=[], ) @@ -130,7 +130,6 @@ def test(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This test function is a proof of work", - "This test function is a proof of work", ), code=code_a, ) @@ -166,7 +165,6 @@ def test_method(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This test function is a proof of concept.", - "This test function is a proof of concept.", ), code=code_b, ) diff --git a/tests/library_analyzer/processing/migration/model/test_inheritance_differ.py b/tests/library_analyzer/processing/migration/model/test_inheritance_differ.py index 51cd6632..a85b2b93 100644 --- a/tests/library_analyzer/processing/migration/model/test_inheritance_differ.py +++ b/tests/library_analyzer/processing/migration/model/test_inheritance_differ.py @@ -42,7 +42,7 @@ class SuperTest: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code=code_a, instance_attributes=[attribute_super], ) @@ -53,7 +53,7 @@ class SuperTest: superclasses=["SuperTest"], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) @@ -87,7 +87,6 @@ def test_function_super(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This is a test function", - "This is a test function", ), code=code_function_a, ) @@ -121,7 +120,7 @@ class SubTest: superclasses=["SuperTest"], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code=code_a, instance_attributes=[attribute_sub], ) @@ -132,7 +131,7 @@ class SubTest: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) @@ -166,7 +165,6 @@ def test_function_sub(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This test function is only for testing", - "This test function is only for testing", ), code=code_function_a, ) diff --git a/tests/library_analyzer/processing/migration/model/test_mapping.py b/tests/library_analyzer/processing/migration/model/test_mapping.py index 7d1ace7d..0f4932a5 100644 --- a/tests/library_analyzer/processing/migration/model/test_mapping.py +++ b/tests/library_analyzer/processing/migration/model/test_mapping.py @@ -21,7 +21,7 @@ def test_one_to_one_mapping() -> None: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) @@ -81,7 +81,7 @@ def test_many_to_many_mapping() -> None: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) @@ -110,7 +110,7 @@ def test_too_different_mapping() -> None: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) @@ -123,7 +123,7 @@ def test_too_different_mapping() -> None: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("not similar to the other class", "not similar to the other class"), + documentation=ClassDocumentation("not similar to the other class"), code=cleandoc( """ @@ -165,7 +165,7 @@ def create_apis() -> tuple[API, API, Class, Class, Class]: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) @@ -178,7 +178,7 @@ def create_apis() -> tuple[API, API, Class, Class, Class]: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) @@ -189,7 +189,7 @@ def create_apis() -> tuple[API, API, Class, Class, Class]: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code="", instance_attributes=[], ) diff --git a/tests/library_analyzer/processing/migration/model/test_strict_differ.py b/tests/library_analyzer/processing/migration/model/test_strict_differ.py index e61cc3be..5e24fb6f 100644 --- a/tests/library_analyzer/processing/migration/model/test_strict_differ.py +++ b/tests/library_analyzer/processing/migration/model/test_strict_differ.py @@ -45,7 +45,7 @@ class Test: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code=code_a, instance_attributes=[attribute_a], ) @@ -64,7 +64,7 @@ class newTest: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a new test", "This is a new test"), + documentation=ClassDocumentation("This is a new test"), code=code_b, instance_attributes=[attribute_b], ) @@ -101,7 +101,6 @@ def test(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This test function is a for testing", - "This test function is a for testing", ), code=code_function_a, ) @@ -139,7 +138,6 @@ def test_method(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This test function is a test", - "This test function is a test", ), code=code_b, ) diff --git a/tests/library_analyzer/processing/migration/model/test_unchanged_differ.py b/tests/library_analyzer/processing/migration/model/test_unchanged_differ.py index f24abf28..8f9d527a 100644 --- a/tests/library_analyzer/processing/migration/model/test_unchanged_differ.py +++ b/tests/library_analyzer/processing/migration/model/test_unchanged_differ.py @@ -35,7 +35,7 @@ class Test: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a test", "This is a test"), + documentation=ClassDocumentation("This is a test"), code=code_a, instance_attributes=[attribute_a], ) @@ -54,7 +54,7 @@ class newTest: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("This is a new test", "This is a new test"), + documentation=ClassDocumentation("This is a new test"), code=code_b, instance_attributes=[attribute_b], ) @@ -91,7 +91,6 @@ def test(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This test function is a for testing", - "This test function is a for testing", ), code=code_function_a, ) @@ -129,7 +128,6 @@ def test_method(test_parameter: str): reexported_by=[], documentation=FunctionDocumentation( "This test function is a test", - "This test function is a test", ), code=code_b, ) diff --git a/tests/migration/annotations/called_after_migration.py b/tests/migration/annotations/called_after_migration.py index 27795664..5b0032e4 100644 --- a/tests/migration/annotations/called_after_migration.py +++ b/tests/migration/annotations/called_after_migration.py @@ -32,7 +32,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_before = Function( @@ -43,7 +43,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after = Function( @@ -54,7 +54,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_before = Function( @@ -65,7 +65,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) mapping_after = OneToOneMapping(1.0, functionv1_after, functionv2_after) @@ -104,7 +104,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_before = Function( @@ -115,7 +115,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after_a = Function( @@ -126,7 +126,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after_b = Function( @@ -137,7 +137,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_before = Function( @@ -148,7 +148,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) mapping_after = OneToManyMapping(1.0, functionv1_after, [functionv2_after_a, functionv2_after_b]) @@ -199,7 +199,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__no_mapping_found() results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after = Function( @@ -210,7 +210,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__no_mapping_found() results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) mapping_after = OneToOneMapping(1.0, functionv1_after, functionv2_after) @@ -248,7 +248,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__before_splits() -> results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_before = Function( @@ -259,7 +259,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__before_splits() -> results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after = Function( @@ -270,7 +270,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__before_splits() -> results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_before_a = Function( @@ -281,7 +281,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__before_splits() -> results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_before_b = Function( @@ -292,7 +292,7 @@ def migrate_called_after_annotation_data_one_to_one_mapping__before_splits() -> results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) mapping_after = OneToOneMapping(1.0, functionv1_after, functionv2_after) @@ -335,7 +335,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping__two_classes() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_before = Function( @@ -346,7 +346,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping__two_classes() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after_a = Function( @@ -357,7 +357,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping__two_classes() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after_b = Function( @@ -368,7 +368,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping__two_classes() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_before_a = Function( @@ -379,7 +379,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping__two_classes() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_before_b = Function( @@ -390,7 +390,7 @@ def migrate_called_after_annotation_data_one_to_many_mapping__two_classes() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) mapping_after = OneToManyMapping(1.0, functionv1_after, [functionv2_after_a, functionv2_after_b]) @@ -441,7 +441,7 @@ def migrate_called_after_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_after_2 = Function( @@ -452,7 +452,7 @@ def migrate_called_after_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_before = Function( @@ -463,7 +463,7 @@ def migrate_called_after_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_after = Function( @@ -474,7 +474,7 @@ def migrate_called_after_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_before = Function( @@ -485,7 +485,7 @@ def migrate_called_after_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) mapping_after = ManyToOneMapping(1.0, [functionv1_after, functionv1_after_2], functionv2_after) diff --git a/tests/migration/annotations/description_migration.py b/tests/migration/annotations/description_migration.py index efc5fa50..334f9985 100644 --- a/tests/migration/annotations/description_migration.py +++ b/tests/migration/annotations/description_migration.py @@ -40,7 +40,7 @@ def migrate_description_annotation_data_one_to_one_mapping__function() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -52,7 +52,7 @@ def migrate_description_annotation_data_one_to_one_mapping__function() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -91,7 +91,7 @@ def migrate_description_annotation_data_one_to_many_mapping__class() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class DescriptionTestClass:\n pass", instance_attributes=[], ) @@ -102,7 +102,7 @@ def migrate_description_annotation_data_one_to_many_mapping__class() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewDescriptionTestClass:\n pass", instance_attributes=[], ) @@ -113,7 +113,7 @@ def migrate_description_annotation_data_one_to_many_mapping__class() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewDescriptionTestClass2:\n pass", instance_attributes=[], ) @@ -125,7 +125,7 @@ def migrate_description_annotation_data_one_to_many_mapping__class() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -229,7 +229,7 @@ def migrate_description_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_2 = Function( @@ -240,7 +240,7 @@ def migrate_description_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -252,7 +252,7 @@ def migrate_description_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) diff --git a/tests/migration/annotations/expert_migration.py b/tests/migration/annotations/expert_migration.py index f118c8d3..87e3a15e 100644 --- a/tests/migration/annotations/expert_migration.py +++ b/tests/migration/annotations/expert_migration.py @@ -40,7 +40,7 @@ def migrate_expert_annotation_data__function() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -52,7 +52,7 @@ def migrate_expert_annotation_data__function() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -89,7 +89,7 @@ def migrate_expert_annotation_data__class() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class ExpertTestClass:\n pass", instance_attributes=[], ) @@ -100,7 +100,7 @@ def migrate_expert_annotation_data__class() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewExpertTestClass:\n pass", instance_attributes=[], ) @@ -112,7 +112,7 @@ def migrate_expert_annotation_data__class() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -201,7 +201,7 @@ def migrate_expert_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_2 = Function( @@ -212,7 +212,7 @@ def migrate_expert_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -224,7 +224,7 @@ def migrate_expert_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) diff --git a/tests/migration/annotations/group_migration.py b/tests/migration/annotations/group_migration.py index b2efb9fc..e7b0d5ba 100644 --- a/tests/migration/annotations/group_migration.py +++ b/tests/migration/annotations/group_migration.py @@ -68,7 +68,7 @@ def migrate_group_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -107,7 +107,7 @@ def migrate_group_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -137,7 +137,7 @@ def migrate_group_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -167,7 +167,7 @@ def migrate_group_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) parameterv2_4_b = Parameter( @@ -187,7 +187,7 @@ def migrate_group_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv2_5 = Function( @@ -198,7 +198,7 @@ def migrate_group_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) classv2_6 = Class( @@ -208,7 +208,7 @@ def migrate_group_annotation_data_one_to_many_mapping() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewClass:\n pass", instance_attributes=[], ) @@ -328,7 +328,7 @@ def migrate_group_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) parameterv1_a = Parameter( @@ -358,7 +358,7 @@ def migrate_group_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) parameterv2_a = Parameter( @@ -423,7 +423,7 @@ def migrate_group_annotation_data_one_to_one_mapping__one_mapping_for_parameters results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) parameterv1_a = Parameter( @@ -453,7 +453,7 @@ def migrate_group_annotation_data_one_to_one_mapping__one_mapping_for_parameters results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) parameterv2_a = Parameter( @@ -571,7 +571,7 @@ def migrate_group_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_2 = Function( @@ -582,7 +582,7 @@ def migrate_group_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -621,7 +621,7 @@ def migrate_group_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) diff --git a/tests/migration/annotations/move_migration.py b/tests/migration/annotations/move_migration.py index 5c6b1a8b..792d20c8 100644 --- a/tests/migration/annotations/move_migration.py +++ b/tests/migration/annotations/move_migration.py @@ -37,7 +37,7 @@ def migrate_move_annotation_data_one_to_one_mapping__global_function() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -49,7 +49,7 @@ def migrate_move_annotation_data_one_to_one_mapping__global_function() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -88,7 +88,7 @@ def migrate_move_annotation_data_one_to_one_mapping__class() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class MoveTestClass:\n pass", instance_attributes=[], ) @@ -99,7 +99,7 @@ def migrate_move_annotation_data_one_to_one_mapping__class() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewMoveTestClass:\n pass", instance_attributes=[], ) @@ -140,7 +140,7 @@ def migrate_move_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -152,7 +152,7 @@ def migrate_move_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -164,7 +164,7 @@ def migrate_move_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -212,7 +212,7 @@ def migrate_move_annotation_data_one_to_one_mapping_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_2 = Function( @@ -223,7 +223,7 @@ def migrate_move_annotation_data_one_to_one_mapping_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -235,7 +235,7 @@ def migrate_move_annotation_data_one_to_one_mapping_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) diff --git a/tests/migration/annotations/remove_migration.py b/tests/migration/annotations/remove_migration.py index c7da6a8b..1074a422 100644 --- a/tests/migration/annotations/remove_migration.py +++ b/tests/migration/annotations/remove_migration.py @@ -37,7 +37,7 @@ def migrate_remove_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -49,7 +49,7 @@ def migrate_remove_annotation_data_one_to_one_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -86,7 +86,7 @@ def migrate_remove_annotation_data_one_to_many_mapping() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class RemoveTestClass:\n pass", instance_attributes=[], ) @@ -97,7 +97,7 @@ def migrate_remove_annotation_data_one_to_many_mapping() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewRemoveTestClass:\n pass", instance_attributes=[], ) @@ -108,7 +108,7 @@ def migrate_remove_annotation_data_one_to_many_mapping() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewRemoveTestClass2:\n pass", instance_attributes=[], ) @@ -120,7 +120,7 @@ def migrate_remove_annotation_data_one_to_many_mapping() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -173,7 +173,7 @@ def migrate_remove_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) functionv1_2 = Function( @@ -184,7 +184,7 @@ def migrate_remove_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) @@ -196,7 +196,7 @@ def migrate_remove_annotation_data_duplicated() -> ( results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) diff --git a/tests/migration/annotations/rename_migration.py b/tests/migration/annotations/rename_migration.py index 1a2f22b8..b6f2e57b 100644 --- a/tests/migration/annotations/rename_migration.py +++ b/tests/migration/annotations/rename_migration.py @@ -109,7 +109,7 @@ def migrate_rename_annotation_data_one_to_many_mapping() -> ( superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class NewClass:\n pass", instance_attributes=[], ) diff --git a/tests/migration/annotations/todo_migration.py b/tests/migration/annotations/todo_migration.py index efa495fa..d90c7294 100644 --- a/tests/migration/annotations/todo_migration.py +++ b/tests/migration/annotations/todo_migration.py @@ -168,7 +168,7 @@ def migrate_todo_annotation_data_many_to_many_mapping() -> tuple[Mapping, Abstra superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="class TestTodoClass:\n pass", instance_attributes=[], ) diff --git a/tests/migration/test_migration.py b/tests/migration/test_migration.py index 0bd15aa9..26da26da 100644 --- a/tests/migration/test_migration.py +++ b/tests/migration/test_migration.py @@ -314,7 +314,7 @@ def test_handle_duplicates() -> None: superclasses=[], is_public=True, reexported_by=[], - documentation=ClassDocumentation("", ""), + documentation=ClassDocumentation(""), code="", instance_attributes=[], ) @@ -395,7 +395,7 @@ def test_was_moved() -> None: results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ) assert _was_moved(function, function, move_annotation) is False @@ -410,7 +410,7 @@ def test_was_moved() -> None: results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ), move_annotation, @@ -428,7 +428,7 @@ def test_was_moved() -> None: results=[], is_public=True, reexported_by=[], - documentation=FunctionDocumentation("", ""), + documentation=FunctionDocumentation(""), code="", ), move_annotation, From dc4485eec731a15f1ef3ab7d6faed34d20b8c2e8 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 3 Apr 2023 19:17:32 +0200 Subject: [PATCH 2/6] style: fix linter issues --- src/library_analyzer/cli/_cli.py | 12 ++--- src/library_analyzer/cli/_run_all.py | 1 - src/library_analyzer/cli/_run_api.py | 3 +- .../processing/api/_ast_visitor.py | 6 +-- .../_create_docstring_parser.py | 13 +++-- .../api/docstring_parsing/_docstring_style.py | 17 +++---- .../api/docstring_parsing/_epydoc_parser.py | 50 ++++++------------- .../api/docstring_parsing/_helpers.py | 7 +-- .../processing/api/model/_api.py | 8 +-- .../docstring_parsing/test_epydoc_parser.py | 31 ++++-------- 10 files changed, 55 insertions(+), 93 deletions(-) diff --git a/src/library_analyzer/cli/_cli.py b/src/library_analyzer/cli/_cli.py index d26e033d..06a66f41 100644 --- a/src/library_analyzer/cli/_cli.py +++ b/src/library_analyzer/cli/_cli.py @@ -72,7 +72,7 @@ def _add_api_subparser(subparsers: _SubParsersAction) -> None: "-s", "--src", help="Directory containing the Python code of the package. If this is omitted, we try to locate the package " - "with the given name in the current Python interpreter.", + "with the given name in the current Python interpreter.", type=Path, required=False, default=None, @@ -85,7 +85,7 @@ def _add_api_subparser(subparsers: _SubParsersAction) -> None: choices=list(DocstringStyle), required=False, default=DocstringStyle.PLAINTEXT.name, - ), + ) def _add_usages_subparser(subparsers: _SubParsersAction) -> None: @@ -156,7 +156,7 @@ def _add_all_subparser(subparsers: _SubParsersAction) -> None: "-s", "--src", help="Directory containing the Python code of the package. If this is omitted, we try to locate the package " - "with the given name in the current Python interpreter.", + "with the given name in the current Python interpreter.", type=Path, required=False, default=None, @@ -176,21 +176,21 @@ def _add_all_subparser(subparsers: _SubParsersAction) -> None: choices=list(DocstringStyle), required=False, default=DocstringStyle.PLAINTEXT.name, - ), + ) all_parser.add_argument( "--processes", help="How many processes should be spawned during processing.", type=int, required=False, default=4, - ), + ) all_parser.add_argument( "--batchsize", help="How many files to process in one go. Higher values lead to higher memory usage but better performance.", type=int, required=False, default=100, - ), + ) def _add_migrate_subparser(subparsers: _SubParsersAction) -> None: diff --git a/src/library_analyzer/cli/_run_all.py b/src/library_analyzer/cli/_run_all.py index a2c24ed4..626b1c3d 100644 --- a/src/library_analyzer/cli/_run_all.py +++ b/src/library_analyzer/cli/_run_all.py @@ -10,7 +10,6 @@ from library_analyzer.processing.api.docstring_parsing import DocstringStyle - def _run_all_command( package: str, src_dir_path: Path, diff --git a/src/library_analyzer/cli/_run_api.py b/src/library_analyzer/cli/_run_api.py index 547fd72c..8d81a850 100644 --- a/src/library_analyzer/cli/_run_api.py +++ b/src/library_analyzer/cli/_run_api.py @@ -1,9 +1,8 @@ from pathlib import Path from library_analyzer.processing.api import get_api -from library_analyzer.processing.dependencies import get_dependencies from library_analyzer.processing.api.docstring_parsing import DocstringStyle - +from library_analyzer.processing.dependencies import get_dependencies from ._read_and_write_file import _write_api_dependency_file, _write_api_file from ._shared_constants import _API_KEY diff --git a/src/library_analyzer/processing/api/_ast_visitor.py b/src/library_analyzer/processing/api/_ast_visitor.py index 12b99805..99ec02b2 100644 --- a/src/library_analyzer/processing/api/_ast_visitor.py +++ b/src/library_analyzer/processing/api/_ast_visitor.py @@ -32,11 +32,7 @@ def trim_code(code: str | None, from_line_no: int, to_line_no: int, encoding: st class _AstVisitor: - def __init__( - self, - docstring_parser: AbstractDocstringParser, - api: API - ) -> None: + def __init__(self, docstring_parser: AbstractDocstringParser, api: API) -> None: self.docstring_parser: AbstractDocstringParser = docstring_parser self.reexported: dict[str, list[str]] = {} self.api: API = api diff --git a/src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py index a400fe78..c2d49741 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_create_docstring_parser.py @@ -1,10 +1,17 @@ -from ._numpydoc_parser import NumpyDocParser -from ._plaintext_docstring_parser import PlaintextDocstringParser +from __future__ import annotations + +from typing import TYPE_CHECKING + from ._docstring_style import DocstringStyle from ._epydoc_parser import EpydocParser +from ._numpydoc_parser import NumpyDocParser +from ._plaintext_docstring_parser import PlaintextDocstringParser + +if TYPE_CHECKING: + from ._abstract_documentation_parser import AbstractDocstringParser -def create_docstring_parser(style: DocstringStyle): +def create_docstring_parser(style: DocstringStyle) -> AbstractDocstringParser: if style == DocstringStyle.NUMPY: return NumpyDocParser() if style == DocstringStyle.EPYDOC: diff --git a/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py index c4c6ca69..3ee5a765 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py @@ -1,20 +1,19 @@ +from __future__ import annotations + from enum import Enum class DocstringStyle(Enum): - # AUTO = "auto", - PLAINTEXT = "plaintext", - # REST = "reST", - NUMPY = "numpy", - # GOOGLE = "google", + PLAINTEXT = ("plaintext",) + NUMPY = ("numpy",) EPYDOC = "epydoc" - def __str__(self): + def __str__(self) -> str: return self.name @staticmethod - def from_string(key: str): + def from_string(key: str) -> DocstringStyle: try: return DocstringStyle[key.upper()] - except KeyError: - raise ValueError(f"Unknown docstring style: {key}") + except KeyError as err: + raise ValueError(f"Unknown docstring style: {key}") from err diff --git a/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py index 42d71581..5a8e8139 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py @@ -1,7 +1,6 @@ -from typing import Optional - import astroid -from docstring_parser import parse as parse_docstring, DocstringStyle, Docstring, DocstringParam +from docstring_parser import Docstring, DocstringParam, DocstringStyle +from docstring_parser import parse as parse_docstring from library_analyzer.processing.api.model import ( ClassDocumentation, @@ -9,8 +8,9 @@ ParameterAssignment, ParameterDocumentation, ) + from ._abstract_documentation_parser import AbstractDocstringParser -from ._helpers import get_full_docstring, get_description +from ._helpers import get_description, get_full_docstring class EpydocParser(AbstractDocstringParser): @@ -20,29 +20,21 @@ class EpydocParser(AbstractDocstringParser): This class is not thread-safe. Each thread should create its own instance. """ - def __init__(self): - self.__cached_function_node: Optional[astroid.FunctionDef] = None - self.__cached_docstring: Optional[DocstringParam] = None + def __init__(self) -> None: + self.__cached_function_node: astroid.FunctionDef | None = None + self.__cached_docstring: DocstringParam | None = None - def get_class_documentation( - self, class_node: astroid.ClassDef - ) -> ClassDocumentation: + def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocumentation: docstring = get_full_docstring(class_node) docstring_obj = parse_docstring(docstring, style=DocstringStyle.EPYDOC) - return ClassDocumentation( - description=get_description(docstring_obj) - ) + return ClassDocumentation(description=get_description(docstring_obj)) - def get_function_documentation( - self, function_node: astroid.FunctionDef - ) -> FunctionDocumentation: + def get_function_documentation(self, function_node: astroid.FunctionDef) -> FunctionDocumentation: docstring = get_full_docstring(function_node) return FunctionDocumentation( - description=get_description( - self.__get_cached_function_numpydoc_string(function_node, docstring) - ) + description=get_description(self.__get_cached_function_numpydoc_string(function_node, docstring)), ) def get_parameter_documentation( @@ -51,25 +43,16 @@ def get_parameter_documentation( parameter_name: str, parameter_assigned_by: ParameterAssignment, ) -> ParameterDocumentation: - # For constructors (__init__ functions) the parameters are described on the class - if function_node.name == "__init__" and isinstance( - function_node.parent, astroid.ClassDef - ): + if function_node.name == "__init__" and isinstance(function_node.parent, astroid.ClassDef): docstring = get_full_docstring(function_node.parent) else: docstring = get_full_docstring(function_node) # Find matching parameter docstrings - function_numpydoc = self.__get_cached_function_numpydoc_string( - function_node, docstring - ) + function_numpydoc = self.__get_cached_function_numpydoc_string(function_node, docstring) all_parameters_numpydoc: list[DocstringParam] = function_numpydoc.params - matching_parameters_numpydoc = [ - it - for it in all_parameters_numpydoc - if it.arg_name == parameter_name - ] + matching_parameters_numpydoc = [it for it in all_parameters_numpydoc if it.arg_name == parameter_name] if len(matching_parameters_numpydoc) == 0: return ParameterDocumentation(type="", default_value="", description="") @@ -81,9 +64,7 @@ def get_parameter_documentation( description=last_parameter_docstring_obj.description, ) - def __get_cached_function_numpydoc_string( - self, function_node: astroid.FunctionDef, docstring: str - ) -> Docstring: + def __get_cached_function_numpydoc_string(self, function_node: astroid.FunctionDef, docstring: str) -> Docstring: """ Returns the NumpyDocString for the given function node. It is only recomputed when the function node differs from the previous one that was passed to this function. This avoids reparsing the docstring for the function @@ -92,7 +73,6 @@ def __get_cached_function_numpydoc_string( On Lars's system this caused a significant performance improvement: Previously, 8.382s were spent inside the function get_parameter_documentation when parsing sklearn. Afterwards, it was only 2.113s. """ - if self.__cached_function_node is not function_node: self.__cached_function_node = function_node self.__cached_docstring = parse_docstring(docstring, style=DocstringStyle.EPYDOC) diff --git a/src/library_analyzer/processing/api/docstring_parsing/_helpers.py b/src/library_analyzer/processing/api/docstring_parsing/_helpers.py index 5bcebe12..546170dc 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_helpers.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_helpers.py @@ -1,18 +1,14 @@ import inspect -from typing import Union import astroid from docstring_parser import Docstring -def get_full_docstring( - declaration: Union[astroid.ClassDef, astroid.FunctionDef] -) -> str: +def get_full_docstring(declaration: astroid.ClassDef | astroid.FunctionDef) -> str: """ Returns the full docstring of the given declaration or an empty string if no docstring is available. Indentation is cleaned up. """ - doc_node = declaration.doc_node if doc_node is None: return "" @@ -24,7 +20,6 @@ def get_description(docstring_obj: Docstring) -> str: Returns the concatenated short and long description of the given docstring object or an empty string if these parts are blank. """ - summary: str = docstring_obj.short_description or "" extended_summary: str = docstring_obj.long_description or "" diff --git a/src/library_analyzer/processing/api/model/_api.py b/src/library_analyzer/processing/api/model/_api.py index 17d008c3..763b7ce3 100644 --- a/src/library_analyzer/processing/api/model/_api.py +++ b/src/library_analyzer/processing/api/model/_api.py @@ -234,9 +234,7 @@ def from_json(json: Any) -> Class: json.get("superclasses", []), json.get("is_public", True), json.get("reexported_by", []), - ClassDocumentation( - description=json.get("description", "") - ), + ClassDocumentation(description=json.get("description", "")), json.get("code", ""), [ Attribute.from_json(instance_attribute, json["id"]) @@ -358,9 +356,7 @@ def from_json(json: Any) -> Function: [Result.from_json(result_json) for result_json in json.get("results", [])], json.get("is_public", True), json.get("reexported_by", []), - FunctionDocumentation( - description=json.get("description", "") - ), + FunctionDocumentation(description=json.get("description", "")), json.get("code", ""), ) diff --git a/tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py b/tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py index 5608db24..1a1bf848 100644 --- a/tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py +++ b/tests/library_analyzer/processing/api/docstring_parsing/test_epydoc_parser.py @@ -9,12 +9,11 @@ ) -@pytest.fixture +@pytest.fixture() def epydoc_parser() -> EpydocParser: return EpydocParser() -# language=python class_with_documentation = ''' class C: """ @@ -26,7 +25,6 @@ class C: """ ''' -# language=python class_without_documentation = """ class C: pass @@ -34,7 +32,7 @@ class C: @pytest.mark.parametrize( - "python_code, expected_class_documentation", + ("python_code", "expected_class_documentation"), [ ( class_with_documentation, @@ -56,7 +54,7 @@ def test_get_class_documentation( epydoc_parser: EpydocParser, python_code: str, expected_class_documentation: ClassDocumentation, -): +) -> None: node = astroid.extract_node(python_code) assert isinstance(node, astroid.ClassDef) @@ -85,13 +83,11 @@ def f(): @pytest.mark.parametrize( - "python_code, expected_function_documentation", + ("python_code", "expected_function_documentation"), [ ( function_with_documentation, - FunctionDocumentation( - description="Lorem ipsum. Code::\n\npass\n\nDolor sit amet." - ), + FunctionDocumentation(description="Lorem ipsum. Code::\n\npass\n\nDolor sit amet."), ), ( function_without_documentation, @@ -107,14 +103,11 @@ def test_get_function_documentation( epydoc_parser: EpydocParser, python_code: str, expected_function_documentation: FunctionDocumentation, -): +) -> None: node = astroid.extract_node(python_code) assert isinstance(node, astroid.FunctionDef) - assert ( - epydoc_parser.get_function_documentation(node) - == expected_function_documentation - ) + assert epydoc_parser.get_function_documentation(node) == expected_function_documentation # language=python @@ -157,7 +150,7 @@ def f(): @pytest.mark.parametrize( - "python_code, parameter_name, parameter_assigned_by, expected_parameter_documentation", + ("python_code", "parameter_name", "parameter_assigned_by", "expected_parameter_documentation"), [ ( class_with_parameters, @@ -231,9 +224,9 @@ def test_get_parameter_documentation( parameter_name: str, parameter_assigned_by: ParameterAssignment, expected_parameter_documentation: ParameterDocumentation, -): +) -> None: node = astroid.extract_node(python_code) - assert isinstance(node, astroid.ClassDef) or isinstance(node, astroid.FunctionDef) + assert isinstance(node, astroid.ClassDef | astroid.FunctionDef) # Find the constructor if isinstance(node, astroid.ClassDef): @@ -243,8 +236,6 @@ def test_get_parameter_documentation( assert isinstance(node, astroid.FunctionDef) assert ( - epydoc_parser.get_parameter_documentation( - node, parameter_name, parameter_assigned_by - ) + epydoc_parser.get_parameter_documentation(node, parameter_name, parameter_assigned_by) == expected_parameter_documentation ) From 73bf7484031e10f6890c9c8aa067eb815be4a7a9 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 3 Apr 2023 19:21:30 +0200 Subject: [PATCH 3/6] style: disable `ERA` rules It has false positives like `# language=python` --- .github/linters/.ruff.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/linters/.ruff.toml b/.github/linters/.ruff.toml index d0c4c012..ec84ebfc 100644 --- a/.github/linters/.ruff.toml +++ b/.github/linters/.ruff.toml @@ -37,7 +37,6 @@ select = [ "INT", "ARG", "PTH", - "ERA", "PGH", "PL", "TRY", From 2acbe456a593bfcd69fff8de83211dd3f7f8be59 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 3 Apr 2023 19:38:05 +0200 Subject: [PATCH 4/6] fix: `NumpyDocParser` --- .../api/docstring_parsing/__init__.py | 3 - .../api/docstring_parsing/_docstring_style.py | 4 +- .../api/docstring_parsing/_epydoc_parser.py | 7 +- .../api/docstring_parsing/_numpydoc_parser.py | 64 ++++++++----------- .../test_get_full_docstring.py | 2 +- .../docstring_parsing/test_numpydoc_parser.py | 4 +- 6 files changed, 34 insertions(+), 50 deletions(-) diff --git a/src/library_analyzer/processing/api/docstring_parsing/__init__.py b/src/library_analyzer/processing/api/docstring_parsing/__init__.py index 257ea18c..af8c8a42 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/__init__.py +++ b/src/library_analyzer/processing/api/docstring_parsing/__init__.py @@ -4,7 +4,6 @@ from ._create_docstring_parser import create_docstring_parser from ._docstring_style import DocstringStyle from ._epydoc_parser import EpydocParser -from ._helpers import get_description, get_full_docstring from ._numpydoc_parser import NumpyDocParser from ._plaintext_docstring_parser import PlaintextDocstringParser @@ -13,8 +12,6 @@ "create_docstring_parser", "DocstringStyle", "EpydocParser", - "get_description", - "get_full_docstring", "NumpyDocParser", "PlaintextDocstringParser", ] diff --git a/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py index 3ee5a765..d370db30 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py @@ -4,8 +4,8 @@ class DocstringStyle(Enum): - PLAINTEXT = ("plaintext",) - NUMPY = ("numpy",) + PLAINTEXT = "plaintext" + NUMPY = "numpy" EPYDOC = "epydoc" def __str__(self) -> str: diff --git a/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py index 5a8e8139..dfcb4921 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py @@ -66,9 +66,10 @@ def get_parameter_documentation( def __get_cached_function_numpydoc_string(self, function_node: astroid.FunctionDef, docstring: str) -> Docstring: """ - Returns the NumpyDocString for the given function node. It is only recomputed when the function node differs - from the previous one that was passed to this function. This avoids reparsing the docstring for the function - itself and all of its parameters. + Return the NumpyDocString for the given function node. + + It is only recomputed when the function node differs from the previous one that was passed to this function. + This avoids reparsing the docstring for the function itself and all of its parameters. On Lars's system this caused a significant performance improvement: Previously, 8.382s were spent inside the function get_parameter_documentation when parsing sklearn. Afterwards, it was only 2.113s. diff --git a/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py index 5e23b0a7..c5fabd6e 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py @@ -1,8 +1,8 @@ import re import astroid -import numpydoc.docscrape -from numpydoc.docscrape import NumpyDocString +from docstring_parser import Docstring, DocstringStyle, DocstringParam +from docstring_parser import parse as parse_docstring from library_analyzer.processing.api.model import ( ClassDocumentation, @@ -10,14 +10,13 @@ ParameterAssignment, ParameterDocumentation, ) - from ._abstract_documentation_parser import AbstractDocstringParser -from ._helpers import get_full_docstring +from ._helpers import get_full_docstring, get_description class NumpyDocParser(AbstractDocstringParser): """ - Parses documentation in the NumpyDoc format. + Parse documentation in the NumpyDoc format. Notes ----- @@ -30,20 +29,22 @@ class NumpyDocParser(AbstractDocstringParser): def __init__(self) -> None: self.__cached_function_node: astroid.FunctionDef | None = None - self.__cached_numpydoc_string: NumpyDocString | None = None + self.__cached_docstring: Docstring | None = None def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocumentation: docstring = get_full_docstring(class_node) + docstring_obj = parse_docstring(docstring, style=DocstringStyle.NUMPYDOC) return ClassDocumentation( - description=_get_description(NumpyDocString(docstring)), + description=get_description(docstring_obj) ) def get_function_documentation(self, function_node: astroid.FunctionDef) -> FunctionDocumentation: docstring = get_full_docstring(function_node) + docstring_obj = self.__get_cached_function_numpydoc_string(function_node, docstring) return FunctionDocumentation( - description=_get_description(self.__get_cached_function_numpydoc_string(function_node, docstring)), + description=get_description(docstring_obj) ) def get_parameter_documentation( @@ -60,7 +61,7 @@ def get_parameter_documentation( # Find matching parameter docstrings function_numpydoc = self.__get_cached_function_numpydoc_string(function_node, docstring) - all_parameters_numpydoc: list[numpydoc.docscrape.Parameter] = function_numpydoc.get("Parameters", []) + all_parameters_numpydoc: list[DocstringParam] = function_numpydoc.params matching_parameters_numpydoc = [ it for it in all_parameters_numpydoc @@ -75,51 +76,36 @@ def get_parameter_documentation( return ParameterDocumentation( type=type_, default_value=default_value, - description="\n".join([line.rstrip() for line in last_parameter_numpydoc.desc]), + description=last_parameter_numpydoc.description, ) def __get_cached_function_numpydoc_string( self, function_node: astroid.FunctionDef, docstring: str, - ) -> NumpyDocString: + ) -> Docstring: """ Return the NumpyDocString for the given function node. It is only recomputed when the function node differs from the previous one that was passed to this function. - This avoids reparsing the docstring for the function itself and all of its parameters. On Lars's system this - caused a significant performance improvement: Previously, 8.382s were spent inside the function - `get_parameter_documentation` when parsing sklearn. Afterwards, it was only 2.113s. + This avoids reparsing the docstring for the function itself and all of its parameters. + + On Lars's system this caused a significant performance improvement: Previously, 8.382s were spent inside the + function `get_parameter_documentation` when parsing sklearn. Afterwards, it was only 2.113s. """ if self.__cached_function_node is not function_node: self.__cached_function_node = function_node - self.__cached_numpydoc_string = NumpyDocString(docstring) - - return self.__cached_numpydoc_string - - -def _get_description(numpydoc_string: NumpyDocString) -> str: - """ - Return the concatenated summary and extended summary parts of the given docstring. - - If these parts are blank, an empty string is returned. - """ - summary: list[str] = numpydoc_string.get("Summary", []) - extended_summary: list[str] = numpydoc_string.get("Extended Summary", []) + self.__cached_docstring = parse_docstring(docstring, style=DocstringStyle.NUMPYDOC) - result = "" - result += "\n".join([line.rstrip() for line in summary]) - result += "\n\n" - result += "\n".join([line.rstrip() for line in extended_summary]) - return result.strip() + return self.__cached_docstring def _is_matching_parameter_numpydoc( - parameter_numpydoc: numpydoc.docscrape.Parameter, + parameter_docstring_obj: DocstringParam, parameter_name: str, parameter_assigned_by: ParameterAssignment, ) -> bool: - """Return whether the given NumpyDoc applied to the parameter with the given name.""" + """Return whether the given docstring object applies to the parameter with the given name.""" if parameter_assigned_by == ParameterAssignment.POSITIONAL_VARARG: lookup_name = f"*{parameter_name}" elif parameter_assigned_by == ParameterAssignment.NAMED_VARARG: @@ -129,17 +115,17 @@ def _is_matching_parameter_numpydoc( # Numpydoc allows multiple parameters to be documented at once. See # https://numpydoc.readthedocs.io/en/latest/format.html#parameters for more information. - return any(name.strip() == lookup_name for name in parameter_numpydoc.name.split(",")) + return any(name.strip() == lookup_name for name in parameter_docstring_obj.arg_name.split(",")) def _get_type_and_default_value( - parameter_numpydoc: numpydoc.docscrape.Parameter, + parameter_docstring_obj: DocstringParam, ) -> tuple[str, str]: """Return the type and default value for the given NumpyDoc.""" - type_ = parameter_numpydoc.type - parts = re.split(r",\s*optional|,\s*default\s*[:=]?", type_) + type_name = parameter_docstring_obj.type_name or "" + parts = re.split(r",\s*optional|,\s*default\s*[:=]?", type_name) if len(parts) != 2: - return type_.strip(), "" + return type_name.strip(), parameter_docstring_obj.default or "" return parts[0].strip(), parts[1].strip() diff --git a/tests/library_analyzer/processing/api/docstring_parsing/test_get_full_docstring.py b/tests/library_analyzer/processing/api/docstring_parsing/test_get_full_docstring.py index f8155948..b9e16801 100644 --- a/tests/library_analyzer/processing/api/docstring_parsing/test_get_full_docstring.py +++ b/tests/library_analyzer/processing/api/docstring_parsing/test_get_full_docstring.py @@ -1,6 +1,6 @@ import astroid import pytest -from library_analyzer.processing.api.docstring_parsing import get_full_docstring +from library_analyzer.processing.api.docstring_parsing._helpers import get_full_docstring class_with_multi_line_documentation = ''' class C: diff --git a/tests/library_analyzer/processing/api/docstring_parsing/test_numpydoc_parser.py b/tests/library_analyzer/processing/api/docstring_parsing/test_numpydoc_parser.py index 1e1cc85f..64c246b7 100644 --- a/tests/library_analyzer/processing/api/docstring_parsing/test_numpydoc_parser.py +++ b/tests/library_analyzer/processing/api/docstring_parsing/test_numpydoc_parser.py @@ -37,7 +37,7 @@ class C: ( class_with_documentation, ClassDocumentation( - description="Lorem ipsum. Code::\n\n pass\n\nDolor sit amet.", + description="Lorem ipsum. Code::\n\npass\n\nDolor sit amet.", ), ), ( @@ -86,7 +86,7 @@ def f(): ( function_with_documentation, FunctionDocumentation( - description="Lorem ipsum. Code::\n\n pass\n\nDolor sit amet.", + description="Lorem ipsum. Code::\n\npass\n\nDolor sit amet.", ), ), ( From f7aab5dbd246d3e5c125bd5168de873221229f16 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 3 Apr 2023 19:46:47 +0200 Subject: [PATCH 5/6] build: remove `numpydoc` --- poetry.lock | 213 +------------------------------------------------ pyproject.toml | 1 - 2 files changed, 2 insertions(+), 212 deletions(-) diff --git a/poetry.lock b/poetry.lock index 32efb981..5504ee8c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,17 +1,5 @@ # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. -[[package]] -name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, -] - [[package]] name = "anyio" version = "3.6.2" @@ -175,18 +163,6 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib- tests = ["attrs[tests-no-zope]", "zope.interface"] tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] -[[package]] -name = "babel" -version = "2.12.1" -description = "Internationalization utilities" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, - {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, -] - [[package]] name = "backcall" version = "0.2.0" @@ -742,18 +718,6 @@ files = [ {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, ] -[[package]] -name = "docutils" -version = "0.19" -description = "Docutils -- Python Documentation Utilities" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, - {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, -] - [[package]] name = "exceptiongroup" version = "1.1.1" @@ -859,18 +823,6 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] - [[package]] name = "iniconfig" version = "2.0.0" @@ -2136,25 +2088,6 @@ files = [ {file = "numpy-1.24.2.tar.gz", hash = "sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22"}, ] -[[package]] -name = "numpydoc" -version = "1.5.0" -description = "Sphinx extension to support docstrings in Numpy format" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "numpydoc-1.5.0-py3-none-any.whl", hash = "sha256:c997759fb6fc32662801cece76491eedbc0ec619b514932ffd2b270ae89c07f9"}, - {file = "numpydoc-1.5.0.tar.gz", hash = "sha256:b0db7b75a32367a0e25c23b397842c65e344a1206524d16c8069f0a1c91b5f4c"}, -] - -[package.dependencies] -Jinja2 = ">=2.10" -sphinx = ">=4.2" - -[package.extras] -testing = ["matplotlib", "pytest", "pytest-cov"] - [[package]] name = "packaging" version = "23.0" @@ -2484,7 +2417,7 @@ email = ["email-validator (>=1.0.3)"] name = "pygments" version = "2.14.0" description = "Pygments is a syntax highlighting package written in Python." -category = "main" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3226,18 +3159,6 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - [[package]] name = "soupsieve" version = "2.4" @@ -3361,136 +3282,6 @@ files = [ {file = "spacy_loggers-1.0.4-py3-none-any.whl", hash = "sha256:e050bf2e63208b2f096b777e494971c962ad7c1dc997641c8f95c622550044ae"}, ] -[[package]] -name = "sphinx" -version = "6.1.3" -description = "Python documentation generator" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "Sphinx-6.1.3.tar.gz", hash = "sha256:0dac3b698538ffef41716cf97ba26c1c7788dba73ce6f150c1ff5b4720786dd2"}, - {file = "sphinx-6.1.3-py3-none-any.whl", hash = "sha256:807d1cb3d6be87eb78a381c3e70ebd8d346b9a25f3753e9947e866b2786865fc"}, -] - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.18,<0.20" -imagesize = ">=1.3" -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.13" -requests = ">=2.25.0" -snowballstemmer = ">=2.0" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] -test = ["cython", "html5lib", "pytest (>=4.6)"] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.4" -description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, - {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.1" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, - {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["html5lib", "pytest"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] - -[package.extras] -test = ["flake8", "mypy", "pytest"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - [[package]] name = "srsly" version = "2.4.6" @@ -4024,4 +3815,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10,<3.12" -content-hash = "21df624b7876d0d2644a04138658818b2b5732a9cb1f607b297e1a92ab80528c" +content-hash = "0deb71d5a186f72860c466ec53aa3d909d25984b6f3b9a26678807e0775d45de" diff --git a/pyproject.toml b/pyproject.toml index d8d81e90..672dcb2f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,6 @@ python = "^3.10,<3.12" astroid = "^2.14.2" black = "^23.1.0" levenshtein = "^0.20.9" -numpydoc = "^1.5" scipy = "^1.10.1" spacy = "^3.5.1" docstring-parser = "^0.15" From 0a292b40bf66ebc731752e312a4b0ce7d1568648 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 3 Apr 2023 19:53:46 +0200 Subject: [PATCH 6/6] style: fix linter issues --- .../api/docstring_parsing/_docstring_style.py | 7 +++++-- .../api/docstring_parsing/_epydoc_parser.py | 2 +- .../processing/api/docstring_parsing/_helpers.py | 10 ++++++---- .../api/docstring_parsing/_numpydoc_parser.py | 13 +++++-------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py index d370db30..6edb137d 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_docstring_style.py @@ -4,8 +4,11 @@ class DocstringStyle(Enum): - PLAINTEXT = "plaintext" - NUMPY = "numpy" + # AUTO = "auto", + PLAINTEXT = ("plaintext",) + # REST = "reST", + NUMPY = ("numpy",) + # GOOGLE = "google", EPYDOC = "epydoc" def __str__(self) -> str: diff --git a/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py index dfcb4921..8865aa17 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_epydoc_parser.py @@ -41,7 +41,7 @@ def get_parameter_documentation( self, function_node: astroid.FunctionDef, parameter_name: str, - parameter_assigned_by: ParameterAssignment, + parameter_assigned_by: ParameterAssignment, # noqa: ARG002 ) -> ParameterDocumentation: # For constructors (__init__ functions) the parameters are described on the class if function_node.name == "__init__" and isinstance(function_node.parent, astroid.ClassDef): diff --git a/src/library_analyzer/processing/api/docstring_parsing/_helpers.py b/src/library_analyzer/processing/api/docstring_parsing/_helpers.py index 546170dc..4803bb9d 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_helpers.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_helpers.py @@ -6,8 +6,9 @@ def get_full_docstring(declaration: astroid.ClassDef | astroid.FunctionDef) -> str: """ - Returns the full docstring of the given declaration or an empty string if no docstring is available. Indentation is - cleaned up. + Return the full docstring of the given declaration. + + If no docstring is available, an empty string is returned. Indentation is cleaned up. """ doc_node = declaration.doc_node if doc_node is None: @@ -17,8 +18,9 @@ def get_full_docstring(declaration: astroid.ClassDef | astroid.FunctionDef) -> s def get_description(docstring_obj: Docstring) -> str: """ - Returns the concatenated short and long description of the given docstring object or an empty string if these parts - are blank. + Return the concatenated short and long description of the given docstring object. + + If these parts are blank, an empty string is returned. """ summary: str = docstring_obj.short_description or "" extended_summary: str = docstring_obj.long_description or "" diff --git a/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py b/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py index c5fabd6e..1a4ccdc2 100644 --- a/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py +++ b/src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py @@ -1,7 +1,7 @@ import re import astroid -from docstring_parser import Docstring, DocstringStyle, DocstringParam +from docstring_parser import Docstring, DocstringParam, DocstringStyle from docstring_parser import parse as parse_docstring from library_analyzer.processing.api.model import ( @@ -10,8 +10,9 @@ ParameterAssignment, ParameterDocumentation, ) + from ._abstract_documentation_parser import AbstractDocstringParser -from ._helpers import get_full_docstring, get_description +from ._helpers import get_description, get_full_docstring class NumpyDocParser(AbstractDocstringParser): @@ -35,17 +36,13 @@ def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocument docstring = get_full_docstring(class_node) docstring_obj = parse_docstring(docstring, style=DocstringStyle.NUMPYDOC) - return ClassDocumentation( - description=get_description(docstring_obj) - ) + return ClassDocumentation(description=get_description(docstring_obj)) def get_function_documentation(self, function_node: astroid.FunctionDef) -> FunctionDocumentation: docstring = get_full_docstring(function_node) docstring_obj = self.__get_cached_function_numpydoc_string(function_node, docstring) - return FunctionDocumentation( - description=get_description(docstring_obj) - ) + return FunctionDocumentation(description=get_description(docstring_obj)) def get_parameter_documentation( self,