Skip to content

numpy

This module defines functions and classes to parse docstrings into structured data.

RE_DOCTEST_BLANKLINE: Pattern ¤

Regular expression to match lines of the form <BLANKLINE>.

RE_DOCTEST_FLAGS: Pattern ¤

Regular expression to match lines containing doctest flags of the form # doctest: +FLAG.

Numpy (Parser) ¤

A Numpy-style docstrings parser.

Source code in pytkdocs/parsers/docstrings/numpy.py
class Numpy(Parser):
    """A Numpy-style docstrings parser."""

    def __init__(self, trim_doctest_flags: bool = True) -> None:
        """
        Initialize the objects.

        Arguments:
            trim_doctest_flags: Whether to remove doctest flags.
        """
        super().__init__()
        self.trim_doctest_flags = trim_doctest_flags
        self.section_reader = {
            Section.Type.PARAMETERS: self.read_parameters_section,
            Section.Type.EXCEPTIONS: self.read_exceptions_section,
            Section.Type.EXAMPLES: self.read_examples_section,
            Section.Type.ATTRIBUTES: self.read_attributes_section,
            Section.Type.RETURN: self.read_return_section,
        }

    def parse_sections(self, docstring: str) -> List[Section]:  # noqa: D102
        if "signature" not in self.context:
            self.context["signature"] = getattr(self.context["obj"], "signature", None)
        if "annotation" not in self.context:
            self.context["annotation"] = getattr(self.context["obj"], "type", empty)
        if "attributes" not in self.context:
            self.context["attributes"] = {}

        docstring_obj = parse(docstring)
        description_all = (
            none_str_cast(docstring_obj.short_description) + "\n\n" + none_str_cast(docstring_obj.long_description)
        ).strip()
        sections = [Section(Section.Type.MARKDOWN, description_all)] if description_all else []
        sections_other = [
            reader(docstring_obj)  # type: ignore
            if sec == Section.Type.RETURN
            else reader(docstring, docstring_obj)  # type: ignore
            for (sec, reader) in self.section_reader.items()
        ]
        sections.extend([sec for sec in sections_other if sec])
        return sections

    def read_parameters_section(
        self,
        docstring: str,
        docstring_obj: Docstring,
    ) -> Optional[Section]:
        """
        Parse a "parameters" section.

        Arguments:
            docstring: The raw docstring.
            docstring_obj: Docstring object parsed by docstring_parser.

        Returns:
            A `Section` object (or `None` if section is empty).
        """
        parameters = []

        docstring_params = [p for p in docstring_obj.params if p.args[0] == "param"]

        for param in docstring_params:
            name = param.arg_name
            kind = None
            type_name = param.type_name
            default = param.default or empty
            try:
                signature_param = self.context["signature"].parameters[name.lstrip("*")]
            except (AttributeError, KeyError):
                self.error(f"No type annotation for parameter '{name}'")
            else:
                if signature_param.annotation is not empty:
                    type_name = signature_param.annotation
                if signature_param.default is not empty:
                    default = signature_param.default
                kind = signature_param.kind

            description = param.description or ""
            if not description:
                self.error(f"No description for parameter '{name}'")

            parameters.append(
                Parameter(
                    name=param.arg_name,
                    annotation=type_name,
                    description=description,
                    default=default,
                    kind=kind,
                )
            )

        if parameters:
            return Section(Section.Type.PARAMETERS, parameters)
        if re.search("Parameters\n", docstring):
            self.error("Empty parameter section")
        return None

    def read_attributes_section(
        self,
        docstring: str,
        docstring_obj: Docstring,
    ) -> Optional[Section]:
        """
        Parse an "attributes" section.

        Arguments:
            docstring: The raw docstring.
            docstring_obj: Docstring object parsed by docstring_parser.

        Returns:
            A `Section` object (or `None` if section is empty).
        """
        attributes = []
        docstring_attributes = [p for p in docstring_obj.params if p.args[0] == "attribute"]

        for attr in docstring_attributes:
            description = attr.description or ""
            if not description:
                self.error(f"No description for attribute '{attr.arg_name}'")
            attributes.append(
                Attribute(
                    name=attr.arg_name,
                    annotation=attr.type_name,
                    description=attr.description,
                )
            )

        if attributes:
            return Section(Section.Type.ATTRIBUTES, attributes)
        if re.search("Attributes\n", docstring):
            self.error("Empty attributes section")
        return None

    def read_exceptions_section(
        self,
        docstring: str,
        docstring_obj: Docstring,
    ) -> Optional[Section]:
        """
        Parse an "exceptions" section.

        Arguments:
            docstring: The raw docstring.
            docstring_obj: Docstring object parsed by docstring_parser.

        Returns:
            A `Section` object (or `None` if section is empty).
        """
        exceptions = []
        except_obj = docstring_obj.raises

        for exception in except_obj:
            description = exception.description or ""
            if not description:
                self.error(f"No description for exception '{exception.type_name}'")
            exceptions.append(AnnotatedObject(exception.type_name, description))

        if exceptions:
            return Section(Section.Type.EXCEPTIONS, exceptions)
        if re.search("Raises\n", docstring):
            self.error("Empty exceptions section")
        return None

    def read_return_section(
        self,
        docstring_obj: Docstring,
    ) -> Optional[Section]:
        """
        Parse a "returns" section.

        Arguments:
            docstring_obj: Docstring object parsed by docstring_parser.

        Returns:
            A `Section` object (or `None` if section is empty).
        """
        if docstring_obj.returns:
            return_obj = docstring_obj.returns

            if return_obj.description:
                description = return_obj.description
            else:
                self.error("Empty return description")
                description = ""

            if self.context["signature"]:
                annotation = self.context["signature"].return_annotation
            else:
                annotation = self.context["annotation"]

            if annotation is empty and return_obj.type_name:
                annotation = return_obj.type_name

            if not annotation:
                self.error("No return type annotation")
                annotation = ""

            if annotation or description:
                return Section(Section.Type.RETURN, AnnotatedObject(annotation, description))

        return None

    def read_examples_section(
        self,
        docstring: str,
        docstring_obj: Docstring,
    ) -> Optional[Section]:
        """
        Parse an "examples" section.

        Arguments:
            docstring: The raw docstring.
            docstring_obj: Docstring object parsed by docstring_parser.

        Returns:
            A `Section` object (or `None` if section is empty).
        """
        text = next(
            (
                meta.description
                for meta in docstring_obj.meta
                if isinstance(meta, DocstringMeta) and meta.args[0] == "examples"
            ),
            "",
        )

        sub_sections = []
        in_code_example = False
        in_code_block = False
        current_text: List[str] = []
        current_example: List[str] = []

        if text:
            for line in text.split("\n"):
                if is_empty_line(line):
                    if in_code_example:
                        if current_example:
                            sub_sections.append((Section.Type.EXAMPLES, "\n".join(current_example)))
                            current_example = []
                        in_code_example = False
                    else:
                        current_text.append(line)

                elif in_code_example:
                    if self.trim_doctest_flags:
                        line = RE_DOCTEST_FLAGS.sub("", line)
                        line = RE_DOCTEST_BLANKLINE.sub("", line)
                    current_example.append(line)

                elif line.startswith("```"):
                    in_code_block = not in_code_block
                    current_text.append(line)

                elif in_code_block:
                    current_text.append(line)

                elif line.startswith(">>>"):
                    if current_text:
                        sub_sections.append((Section.Type.MARKDOWN, "\n".join(current_text)))
                        current_text = []
                    in_code_example = True

                    if self.trim_doctest_flags:
                        line = RE_DOCTEST_FLAGS.sub("", line)
                    current_example.append(line)
                else:
                    current_text.append(line)

        if current_text:
            sub_sections.append((Section.Type.MARKDOWN, "\n".join(current_text)))
        elif current_example:
            sub_sections.append((Section.Type.EXAMPLES, "\n".join(current_example)))

        if sub_sections:
            return Section(Section.Type.EXAMPLES, sub_sections)

        if re.search("Examples\n", docstring):
            self.error("Empty examples section")
        return None

__init__(self, trim_doctest_flags=True) special ¤

Initialize the objects.

Parameters:

Name Type Description Default
trim_doctest_flags bool

Whether to remove doctest flags.

True
Source code in pytkdocs/parsers/docstrings/numpy.py
def __init__(self, trim_doctest_flags: bool = True) -> None:
    """
    Initialize the objects.

    Arguments:
        trim_doctest_flags: Whether to remove doctest flags.
    """
    super().__init__()
    self.trim_doctest_flags = trim_doctest_flags
    self.section_reader = {
        Section.Type.PARAMETERS: self.read_parameters_section,
        Section.Type.EXCEPTIONS: self.read_exceptions_section,
        Section.Type.EXAMPLES: self.read_examples_section,
        Section.Type.ATTRIBUTES: self.read_attributes_section,
        Section.Type.RETURN: self.read_return_section,
    }

parse_sections(self, docstring) ¤

Parse a docstring as a list of sections.

Parameters:

Name Type Description Default
docstring str

The docstring to parse.

required

Returns:

Type Description
List[pytkdocs.parsers.docstrings.base.Section]

A list of Sections.

Source code in pytkdocs/parsers/docstrings/numpy.py
def parse_sections(self, docstring: str) -> List[Section]:  # noqa: D102
    if "signature" not in self.context:
        self.context["signature"] = getattr(self.context["obj"], "signature", None)
    if "annotation" not in self.context:
        self.context["annotation"] = getattr(self.context["obj"], "type", empty)
    if "attributes" not in self.context:
        self.context["attributes"] = {}

    docstring_obj = parse(docstring)
    description_all = (
        none_str_cast(docstring_obj.short_description) + "\n\n" + none_str_cast(docstring_obj.long_description)
    ).strip()
    sections = [Section(Section.Type.MARKDOWN, description_all)] if description_all else []
    sections_other = [
        reader(docstring_obj)  # type: ignore
        if sec == Section.Type.RETURN
        else reader(docstring, docstring_obj)  # type: ignore
        for (sec, reader) in self.section_reader.items()
    ]
    sections.extend([sec for sec in sections_other if sec])
    return sections

read_attributes_section(self, docstring, docstring_obj) ¤

Parse an "attributes" section.

Parameters:

Name Type Description Default
docstring str

The raw docstring.

required
docstring_obj Docstring

Docstring object parsed by docstring_parser.

required

Returns:

Type Description
Optional[pytkdocs.parsers.docstrings.base.Section]

A Section object (or None if section is empty).

Source code in pytkdocs/parsers/docstrings/numpy.py
def read_attributes_section(
    self,
    docstring: str,
    docstring_obj: Docstring,
) -> Optional[Section]:
    """
    Parse an "attributes" section.

    Arguments:
        docstring: The raw docstring.
        docstring_obj: Docstring object parsed by docstring_parser.

    Returns:
        A `Section` object (or `None` if section is empty).
    """
    attributes = []
    docstring_attributes = [p for p in docstring_obj.params if p.args[0] == "attribute"]

    for attr in docstring_attributes:
        description = attr.description or ""
        if not description:
            self.error(f"No description for attribute '{attr.arg_name}'")
        attributes.append(
            Attribute(
                name=attr.arg_name,
                annotation=attr.type_name,
                description=attr.description,
            )
        )

    if attributes:
        return Section(Section.Type.ATTRIBUTES, attributes)
    if re.search("Attributes\n", docstring):
        self.error("Empty attributes section")
    return None

read_examples_section(self, docstring, docstring_obj) ¤

Parse an "examples" section.

Parameters:

Name Type Description Default
docstring str

The raw docstring.

required
docstring_obj Docstring

Docstring object parsed by docstring_parser.

required

Returns:

Type Description
Optional[pytkdocs.parsers.docstrings.base.Section]

A Section object (or None if section is empty).

Source code in pytkdocs/parsers/docstrings/numpy.py
def read_examples_section(
    self,
    docstring: str,
    docstring_obj: Docstring,
) -> Optional[Section]:
    """
    Parse an "examples" section.

    Arguments:
        docstring: The raw docstring.
        docstring_obj: Docstring object parsed by docstring_parser.

    Returns:
        A `Section` object (or `None` if section is empty).
    """
    text = next(
        (
            meta.description
            for meta in docstring_obj.meta
            if isinstance(meta, DocstringMeta) and meta.args[0] == "examples"
        ),
        "",
    )

    sub_sections = []
    in_code_example = False
    in_code_block = False
    current_text: List[str] = []
    current_example: List[str] = []

    if text:
        for line in text.split("\n"):
            if is_empty_line(line):
                if in_code_example:
                    if current_example:
                        sub_sections.append((Section.Type.EXAMPLES, "\n".join(current_example)))
                        current_example = []
                    in_code_example = False
                else:
                    current_text.append(line)

            elif in_code_example:
                if self.trim_doctest_flags:
                    line = RE_DOCTEST_FLAGS.sub("", line)
                    line = RE_DOCTEST_BLANKLINE.sub("", line)
                current_example.append(line)

            elif line.startswith("```"):
                in_code_block = not in_code_block
                current_text.append(line)

            elif in_code_block:
                current_text.append(line)

            elif line.startswith(">>>"):
                if current_text:
                    sub_sections.append((Section.Type.MARKDOWN, "\n".join(current_text)))
                    current_text = []
                in_code_example = True

                if self.trim_doctest_flags:
                    line = RE_DOCTEST_FLAGS.sub("", line)
                current_example.append(line)
            else:
                current_text.append(line)

    if current_text:
        sub_sections.append((Section.Type.MARKDOWN, "\n".join(current_text)))
    elif current_example:
        sub_sections.append((Section.Type.EXAMPLES, "\n".join(current_example)))

    if sub_sections:
        return Section(Section.Type.EXAMPLES, sub_sections)

    if re.search("Examples\n", docstring):
        self.error("Empty examples section")
    return None

read_exceptions_section(self, docstring, docstring_obj) ¤

Parse an "exceptions" section.

Parameters:

Name Type Description Default
docstring str

The raw docstring.

required
docstring_obj Docstring

Docstring object parsed by docstring_parser.

required

Returns:

Type Description
Optional[pytkdocs.parsers.docstrings.base.Section]

A Section object (or None if section is empty).

Source code in pytkdocs/parsers/docstrings/numpy.py
def read_exceptions_section(
    self,
    docstring: str,
    docstring_obj: Docstring,
) -> Optional[Section]:
    """
    Parse an "exceptions" section.

    Arguments:
        docstring: The raw docstring.
        docstring_obj: Docstring object parsed by docstring_parser.

    Returns:
        A `Section` object (or `None` if section is empty).
    """
    exceptions = []
    except_obj = docstring_obj.raises

    for exception in except_obj:
        description = exception.description or ""
        if not description:
            self.error(f"No description for exception '{exception.type_name}'")
        exceptions.append(AnnotatedObject(exception.type_name, description))

    if exceptions:
        return Section(Section.Type.EXCEPTIONS, exceptions)
    if re.search("Raises\n", docstring):
        self.error("Empty exceptions section")
    return None

read_parameters_section(self, docstring, docstring_obj) ¤

Parse a "parameters" section.

Parameters:

Name Type Description Default
docstring str

The raw docstring.

required
docstring_obj Docstring

Docstring object parsed by docstring_parser.

required

Returns:

Type Description
Optional[pytkdocs.parsers.docstrings.base.Section]

A Section object (or None if section is empty).

Source code in pytkdocs/parsers/docstrings/numpy.py
def read_parameters_section(
    self,
    docstring: str,
    docstring_obj: Docstring,
) -> Optional[Section]:
    """
    Parse a "parameters" section.

    Arguments:
        docstring: The raw docstring.
        docstring_obj: Docstring object parsed by docstring_parser.

    Returns:
        A `Section` object (or `None` if section is empty).
    """
    parameters = []

    docstring_params = [p for p in docstring_obj.params if p.args[0] == "param"]

    for param in docstring_params:
        name = param.arg_name
        kind = None
        type_name = param.type_name
        default = param.default or empty
        try:
            signature_param = self.context["signature"].parameters[name.lstrip("*")]
        except (AttributeError, KeyError):
            self.error(f"No type annotation for parameter '{name}'")
        else:
            if signature_param.annotation is not empty:
                type_name = signature_param.annotation
            if signature_param.default is not empty:
                default = signature_param.default
            kind = signature_param.kind

        description = param.description or ""
        if not description:
            self.error(f"No description for parameter '{name}'")

        parameters.append(
            Parameter(
                name=param.arg_name,
                annotation=type_name,
                description=description,
                default=default,
                kind=kind,
            )
        )

    if parameters:
        return Section(Section.Type.PARAMETERS, parameters)
    if re.search("Parameters\n", docstring):
        self.error("Empty parameter section")
    return None

read_return_section(self, docstring_obj) ¤

Parse a "returns" section.

Parameters:

Name Type Description Default
docstring_obj Docstring

Docstring object parsed by docstring_parser.

required

Returns:

Type Description
Optional[pytkdocs.parsers.docstrings.base.Section]

A Section object (or None if section is empty).

Source code in pytkdocs/parsers/docstrings/numpy.py
def read_return_section(
    self,
    docstring_obj: Docstring,
) -> Optional[Section]:
    """
    Parse a "returns" section.

    Arguments:
        docstring_obj: Docstring object parsed by docstring_parser.

    Returns:
        A `Section` object (or `None` if section is empty).
    """
    if docstring_obj.returns:
        return_obj = docstring_obj.returns

        if return_obj.description:
            description = return_obj.description
        else:
            self.error("Empty return description")
            description = ""

        if self.context["signature"]:
            annotation = self.context["signature"].return_annotation
        else:
            annotation = self.context["annotation"]

        if annotation is empty and return_obj.type_name:
            annotation = return_obj.type_name

        if not annotation:
            self.error("No return type annotation")
            annotation = ""

        if annotation or description:
            return Section(Section.Type.RETURN, AnnotatedObject(annotation, description))

    return None

is_empty_line(line) ¤

Tell if a line is empty.

Parameters:

Name Type Description Default
line str

The line to check.

required

Returns:

Type Description
bool

True if the line is empty or composed of blanks only, False otherwise.

Source code in pytkdocs/parsers/docstrings/numpy.py
def is_empty_line(line: str) -> bool:
    """
    Tell if a line is empty.

    Arguments:
        line: The line to check.

    Returns:
        True if the line is empty or composed of blanks only, False otherwise.
    """
    return not line.strip()
Back to top