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), )
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