예제 #1
0
    def read_return_section(self, lines: List[str], start_index: int) -> Tuple[Optional[Section], int]:
        """
        Parse an "returns" section.

        Arguments:
            lines: The return block lines.
            start_index: The line number to start at.

        Returns:
            A tuple containing a `Section` (or `None`) and the index at which to continue parsing.
        """
        text, i = self.read_block(lines, start_index)

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

        if annotation is empty:
            if not text:
                self.error("No return type annotation")
            else:
                try:
                    type_, text = text.split(":", 1)
                except ValueError:
                    self.error("No type in return description")
                else:
                    annotation = type_.lstrip()
                    text = text.lstrip()

        if annotation is empty and not text:
            self.error(f"Empty return section at line {start_index}")
            return None, i

        return Section(Section.Type.RETURN, AnnotatedObject(annotation, text)), i
예제 #2
0
    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 tuple containing a `Section` (or `None`).
        """
        return_obj = docstring_obj.returns if docstring_obj.returns else []
        text = return_obj.description if return_obj else ""

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

        if annotation is empty:
            if text:
                annotation = return_obj.type_name or empty
                text = return_obj.description
            elif return_obj and annotation is empty:
                self.error("No return type annotation")

        if return_obj and not text:
            self.error("Empty return description")
        if not return_obj or annotation is empty or not text:
            return None
        return Section(Section.Type.RETURN, AnnotatedObject(annotation, text))
예제 #3
0
    def read_exceptions_section(self, lines: List[str], start_index: int) -> Tuple[Optional[Section], int]:
        """
        Parse an "exceptions" section.

        Arguments:
            lines: The exceptions block lines.
            start_index: The line number to start at.

        Returns:
            A tuple containing a `Section` (or `None`) and the index at which to continue parsing.
        """
        exceptions = []
        block, i = self.read_block_items(lines, start_index)

        for exception_line in block:
            try:
                annotation, description = exception_line.split(": ", 1)
            except ValueError:
                self.error(f"Failed to get 'exception: description' pair from '{exception_line}'")
            else:
                exceptions.append(AnnotatedObject(annotation, description.lstrip(" ")))

        if exceptions:
            return Section(Section.Type.EXCEPTIONS, exceptions), i

        self.error(f"Empty exceptions section at line {start_index}")
        return None, i
예제 #4
0
    def _read_return(self, lines: List[str], start_index: int) -> int:
        """
        Parse an return value.

        Arguments:
            lines: The docstring lines.
            start_index: The line number to start at.

        Returns:
            Index at which to continue parsing.
        """
        parsed_directive = self._parse_directive(lines, start_index)
        if parsed_directive.invalid:
            return parsed_directive.next_index

        annotation = empty
        # Annotation precedence:
        # - signature annotation
        # - "rtype" directive type
        # - external context type TODO: spend time understanding where this comes from
        # - empty
        if self._typed_context.signature is not None and self._typed_context.signature.return_annotation is not empty:
            annotation = self._typed_context.signature.return_annotation
        elif self._parsed_values.return_type is not None:
            annotation = self._parsed_values.return_type
        else:
            annotation = self._typed_context.annotation

        self._parsed_values.return_value = AnnotatedObject(
            annotation, parsed_directive.value)

        return parsed_directive.next_index
예제 #5
0
    def read_exceptions_section(
        self,
        docstring: str,
        docstring_obj: Docstring,
    ) -> Optional[Section]:
        """
        Parse an "exceptions" section.

        Arguments:
            docstring_obj: Docstring object parsed by docstring_parser.

        Returns:
            A tuple containing a `Section` (or `None`) and the index at which to continue parsing.
        """
        exceptions = []
        except_obj = docstring_obj.raises

        for exception in except_obj:
            exceptions.append(
                AnnotatedObject(exception.type_name, exception.description))

        if exceptions:
            return Section(Section.Type.EXCEPTIONS, exceptions)
        if re.search("Raises\n", docstring):
            self.error("Empty exceptions section")
        return None
예제 #6
0
    def _read_exception(self, lines: List[str], start_index: int) -> int:
        """
        Parse an exceptions value.

        Arguments:
            lines: The docstring lines.
            start_index: The line number to start at.

        Returns:
            A tuple containing a `Section` (or `None`) and the index at which to continue parsing.
        """
        parsed_directive = self._parse_directive(lines, start_index)
        if parsed_directive.invalid:
            return parsed_directive.next_index

        if len(parsed_directive.directive_parts) == 2:
            ex_type = parsed_directive.directive_parts[1]
            self._parsed_values.exceptions.append(
                AnnotatedObject(ex_type, parsed_directive.value))
        else:
            self.error(
                f"Failed to parse exception directive from '{parsed_directive.line}'"
            )

        return parsed_directive.next_index
예제 #7
0
def test_member_function___expected_return():
    class_ = get_rst_object_documentation("class_docstrings:ClassWithFunction")
    init = class_.methods[0]
    sections = init.docstring_sections
    assert len(sections) == 3
    assert sections[2].type == Section.Type.RETURN
    assert_annotated_obj_equal(sections[2].value,
                               AnnotatedObject(str, "Concatenated result"))
예제 #8
0
def test_property_docstring__expected_return():
    """Parse a property docstring."""
    class_ = get_rst_object_documentation("class_docstrings:NotDefinedYet")
    prop = class_.attributes[0]
    sections = prop.docstring_sections
    assert len(sections) == 2
    assert sections[1].type == Section.Type.RETURN
    assert_annotated_obj_equal(sections[1].value,
                               AnnotatedObject("NotDefinedYet", "self!"))
예제 #9
0
def test_parse__all_exception_names__param_section(attribute_directive_name):
    sections, errors = parse_detailed(f"""
        Docstring with one line attribute.

        :{attribute_directive_name} {SOME_EXCEPTION_NAME}: {SOME_TEXT}
        """)
    assert len(sections) == 2
    assert sections[1].type == Section.Type.EXCEPTIONS
    assert_annotated_obj_equal(
        sections[1].value[0],
        AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT),
    )
예제 #10
0
def test_parse__multiple_raises_directive__exception_section_with_two():
    def f(foo: str):
        """
        Function with only return directive

        :raise SomeException: descriptive test text
        :raise SomeOtherException: descriptive test text
        """
        return foo

    sections, errors = parse(f)
    assert len(sections) == 2
    assert sections[1].type == Section.Type.EXCEPTIONS
    assert_annotated_obj_equal(
        sections[1].value[0],
        AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT),
    )
    assert_annotated_obj_equal(
        sections[1].value[1],
        AnnotatedObject(annotation=SOME_OTHER_EXCEPTION_NAME,
                        description=SOME_TEXT),
    )
예제 #11
0
def test_parse__return_directive_annotation__return_section_with_type():
    def f(foo: str) -> str:
        """
        Function with return directive, rtype directive, & annotation

        :return: descriptive test text
        """
        return foo

    sections, errors = parse(f)
    assert len(sections) == 2
    assert sections[1].type == Section.Type.RETURN
    assert_annotated_obj_equal(
        sections[1].value,
        AnnotatedObject(annotation=str, description=SOME_TEXT),
    )
예제 #12
0
def test_parse__return_directive_rtype_first__return_section_with_type():
    def f(foo: str):
        """
        Function with only return & rtype directive

        :rtype: str
        :return: descriptive test text
        """
        return foo

    sections, errors = parse(f)
    assert len(sections) == 2
    assert sections[1].type == Section.Type.RETURN
    assert_annotated_obj_equal(
        sections[1].value,
        AnnotatedObject(annotation="str", description=SOME_TEXT),
    )