Skip to content

NumpyDocParser

Bases: AbstractDocstringParser

Parse documentation in the NumpyDoc format.

Notes

This class is not thread-safe. Each thread should create its own instance.

References

.. [1] https://numpydoc.readthedocs.io/en/latest/format.html

Source code in src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py
class NumpyDocParser(AbstractDocstringParser):
    """
    Parse documentation in the NumpyDoc format.

    Notes
    -----
    This class is not thread-safe. Each thread should create its own instance.

    References
    ----------
    .. [1] https://numpydoc.readthedocs.io/en/latest/format.html
    """

    def __init__(self) -> None:
        self.__cached_function_node: astroid.FunctionDef | None = None
        self.__cached_docstring: Docstring | None = None

    def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocstring:
        docstring = get_full_docstring(class_node)
        docstring_obj = parse_docstring(docstring, style=DocstringStyle.NUMPYDOC)

        return ClassDocstring(
            description=get_description(docstring_obj),
            full_docstring=docstring,
        )

    def get_function_documentation(self, function_node: astroid.FunctionDef) -> FunctionDocstring:
        docstring = get_full_docstring(function_node)
        docstring_obj = self.__get_cached_function_numpydoc_string(function_node, docstring)

        return FunctionDocstring(
            description=get_description(docstring_obj),
            full_docstring=docstring,
        )

    def get_parameter_documentation(
        self,
        function_node: astroid.FunctionDef,
        parameter_name: str,
        parameter_assigned_by: ParameterAssignment,
    ) -> ParameterDocstring:
        # 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 _is_matching_parameter_numpydoc(it, parameter_name, parameter_assigned_by)
        ]

        if len(matching_parameters_numpydoc) == 0:
            return ParameterDocstring(type="", default_value="", description="")

        last_parameter_numpydoc = matching_parameters_numpydoc[-1]
        type_, default_value = _get_type_and_default_value(last_parameter_numpydoc)
        return ParameterDocstring(
            type=type_ or "",
            default_value=default_value or "",
            description=last_parameter_numpydoc.description or "",
        )

    def __get_cached_function_numpydoc_string(
        self,
        function_node: astroid.FunctionDef,
        docstring: str,
    ) -> 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.
        """
        if self.__cached_function_node is not function_node:
            self.__cached_function_node = function_node
            self.__cached_docstring = parse_docstring(docstring, style=DocstringStyle.NUMPYDOC)

        return self.__cached_docstring

__cached_docstring: Docstring | None = None instance-attribute

__cached_function_node: astroid.FunctionDef | None = None instance-attribute

__get_cached_function_numpydoc_string(function_node, 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.

Source code in src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py
def __get_cached_function_numpydoc_string(
    self,
    function_node: astroid.FunctionDef,
    docstring: str,
) -> 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.
    """
    if self.__cached_function_node is not function_node:
        self.__cached_function_node = function_node
        self.__cached_docstring = parse_docstring(docstring, style=DocstringStyle.NUMPYDOC)

    return self.__cached_docstring

__init__()

Source code in src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py
def __init__(self) -> None:
    self.__cached_function_node: astroid.FunctionDef | None = None
    self.__cached_docstring: Docstring | None = None

get_class_documentation(class_node)

Source code in src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py
def get_class_documentation(self, class_node: astroid.ClassDef) -> ClassDocstring:
    docstring = get_full_docstring(class_node)
    docstring_obj = parse_docstring(docstring, style=DocstringStyle.NUMPYDOC)

    return ClassDocstring(
        description=get_description(docstring_obj),
        full_docstring=docstring,
    )

get_function_documentation(function_node)

Source code in src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py
def get_function_documentation(self, function_node: astroid.FunctionDef) -> FunctionDocstring:
    docstring = get_full_docstring(function_node)
    docstring_obj = self.__get_cached_function_numpydoc_string(function_node, docstring)

    return FunctionDocstring(
        description=get_description(docstring_obj),
        full_docstring=docstring,
    )

get_parameter_documentation(function_node, parameter_name, parameter_assigned_by)

Source code in src/library_analyzer/processing/api/docstring_parsing/_numpydoc_parser.py
def get_parameter_documentation(
    self,
    function_node: astroid.FunctionDef,
    parameter_name: str,
    parameter_assigned_by: ParameterAssignment,
) -> ParameterDocstring:
    # 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 _is_matching_parameter_numpydoc(it, parameter_name, parameter_assigned_by)
    ]

    if len(matching_parameters_numpydoc) == 0:
        return ParameterDocstring(type="", default_value="", description="")

    last_parameter_numpydoc = matching_parameters_numpydoc[-1]
    type_, default_value = _get_type_and_default_value(last_parameter_numpydoc)
    return ParameterDocstring(
        type=type_ or "",
        default_value=default_value or "",
        description=last_parameter_numpydoc.description or "",
    )