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