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

        Arguments:
            docstring_obj: Docstring object parsed by docstring_parser.

        Returns:
            A tuple containing a `Section` (or `None`).
        """
        attributes = []
        docstring_attributes = [
            p for p in docstring_obj.params if p.args[0] == "attribute"
        ]

        for attr in docstring_attributes:
            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 test_parse__attribute_field_multi_line__param_section(docstring):
    """Parse a simple docstring."""
    sections, errors = parse_detailed(docstring)
    assert len(sections) == 2
    assert sections[1].type == Section.Type.ATTRIBUTES
    assert_attribute_equal(
        sections[1].value[0],
        Attribute(SOME_NAME,
                  annotation=empty,
                  description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}"),
    )
def test_parse__all_attribute_names__param_section(attribute_directive_name):
    sections, errors = parse_detailed(f"""
        Docstring with one line attribute.

        :{attribute_directive_name} {SOME_NAME}: {SOME_TEXT}
        """)
    assert len(sections) == 2
    assert sections[1].type == Section.Type.ATTRIBUTES
    assert_attribute_equal(
        sections[1].value[0],
        Attribute(SOME_NAME, annotation=empty, description=SOME_TEXT),
    )
    def _read_attribute(self, lines: List[str], start_index: int) -> int:
        """
        Parse an attribute 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

        if len(parsed_directive.directive_parts) == 2:
            name = parsed_directive.directive_parts[1]
        else:
            self.error(
                f"Failed to parse field directive from '{parsed_directive.line}'"
            )
            return parsed_directive.next_index

        annotation = empty

        # Annotation precedence:
        # - external context type TODO: spend time understanding where this comes from
        # - "vartype" directive type
        # - empty

        parsed_attribute_type = self._parsed_values.attribute_types.get(name)
        if parsed_attribute_type is not None:
            annotation = parsed_attribute_type

        context_attribute_annotation = self._typed_context.attributes[
            name].get("annotation")
        if context_attribute_annotation is not None:
            annotation = context_attribute_annotation

        if name in self._parsed_values.attributes:
            self.errors.append(f"Duplicate attribute entry for '{name}'")
        else:
            self._parsed_values.attributes[name] = Attribute(
                name=name,
                annotation=annotation,
                description=parsed_directive.value,
            )

        return parsed_directive.next_index
def test_parse__class_attributes__attributes_section():
    class Foo:
        """
        Class docstring with attributes

        :var foo: descriptive test text
        """

    sections, errors = parse(Foo)
    assert len(sections) == 2
    assert sections[1].type == Section.Type.ATTRIBUTES
    assert_attribute_equal(
        sections[1].value[0],
        Attribute(SOME_NAME, annotation=empty, description=SOME_TEXT),
    )
Exemple #6
0
    def read_attributes_section(
            self, lines: List[str],
            start_index: int) -> Tuple[Optional[Section], int]:
        """
        Parse an "attributes" section.

        Arguments:
            lines: The parameters 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.
        """
        attributes = []
        block, i = self.read_block_items(lines, start_index)

        for attr_line in block:
            try:
                name_with_type, description = attr_line.split(":", 1)
            except ValueError:
                self.error(
                    f"Failed to get 'name: description' pair from '{attr_line}'"
                )
                continue

            description = description.lstrip()

            if " " in name_with_type:
                name, annotation = name_with_type.split(" ", 1)
                annotation = annotation.strip("()")
                if annotation.endswith(", optional"):
                    annotation = annotation[:-10]
            else:
                name = name_with_type
                annotation = self.context["attributes"].get(name, {}).get(
                    "annotation", empty)

            attributes.append(
                Attribute(name=name,
                          annotation=annotation,
                          description=description))

        if attributes:
            return Section(Section.Type.ATTRIBUTES, attributes), i

        self.error(f"Empty attributes section at line {start_index}")
        return None, i