def read_parameters_section(self, lines: List[str], start_index: int) -> Tuple[Optional[Section], int]: """ Parse a "parameters" 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. """ parameters = [] type_: Any block, i = self.read_block_items(lines, start_index) for param_line in block: try: name_with_type, description = param_line.split(":", 1) except ValueError: self.error(f"Failed to get 'name: description' pair from '{param_line}'") continue description = description.lstrip() if " " in name_with_type: name, type_ = name_with_type.split(" ", 1) type_ = type_.strip("()") if type_.endswith(", optional"): type_ = type_[:-10] else: name = name_with_type type_ = empty default = empty annotation = type_ kind = None try: signature_param = self.context["signature"].parameters[name.lstrip("*")] # type: ignore except (AttributeError, KeyError): self.error(f"No type annotation for parameter '{name}'") else: if signature_param.annotation is not empty: annotation = signature_param.annotation if signature_param.default is not empty: default = signature_param.default kind = signature_param.kind parameters.append( Parameter(name=name, annotation=annotation, description=description, default=default, kind=kind) ) if parameters: return Section(Section.Type.PARAMETERS, parameters), i self.error(f"Empty parameters section at line {start_index}") return None, i
def test_parse__param_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.PARAMETERS assert_parameter_equal( sections[1].value[0], Parameter(SOME_NAME, annotation=empty, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}", kind=empty), )
def test_member_function___expected_param(): class_ = get_rst_object_documentation("class_docstrings:ClassWithFunction") init = class_.methods[0] sections = init.docstring_sections assert len(sections) == 3 param_section = sections[1] assert param_section.type == Section.Type.PARAMETERS assert_parameter_equal( param_section.value[0], Parameter("value", str, "Value to store", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)) assert_parameter_equal( param_section.value[1], Parameter("other", "int", "Other value with default", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, default=1), )
def read_parameters_section( self, docstring: str, docstring_obj: Docstring, ) -> Optional[Section]: """ Parse a "parameters" 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. """ 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("*")] # type: ignore 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 parameters.append( Parameter( name=param.arg_name, annotation=type_name, description=param.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 test_parse__all_param_names__param_section(param_directive_name): sections, errors = parse_detailed(f""" Docstring with one line param. :{param_directive_name} {SOME_NAME}: {SOME_TEXT} """) assert len(sections) == 2 assert sections[1].type == Section.Type.PARAMETERS assert_parameter_equal( sections[1].value[0], Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=empty))
def test_parse__param_field__param_section(): """Parse a simple docstring.""" sections, errors = parse_detailed(f""" Docstring with one line param. :param {SOME_NAME}: {SOME_TEXT} """) assert len(sections) == 2 assert sections[1].type == Section.Type.PARAMETERS assert_parameter_equal( sections[1].value[0], Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=empty))
def test_parse__param_field_no_matching_param__result_from_docstring(): """Parse a simple docstring.""" def f(foo: str): """ Docstring with line continuation. :param other: descriptive test text """ sections, errors = parse(f) assert len(sections) == 2 assert sections[1].type == Section.Type.PARAMETERS assert_parameter_equal( sections[1].value[0], Parameter("other", annotation=empty, description=SOME_TEXT, kind=empty), )
def _read_parameter(self, lines: List[str], start_index: int) -> int: """ Parse a parameter 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 directive_type = None if len(parsed_directive.directive_parts) == 2: # no type info name = parsed_directive.directive_parts[1] elif len(parsed_directive.directive_parts) == 3: directive_type = parsed_directive.directive_parts[1] name = parsed_directive.directive_parts[2] else: self.error( f"Failed to parse field directive from '{parsed_directive.line}'" ) return parsed_directive.next_index if name in self._parsed_values.parameters: self.errors.append(f"Duplicate parameter entry for '{name}'") return parsed_directive.next_index annotation = self._determine_param_annotation(name, directive_type) default, kind = self._determine_param_details(name) self._parsed_values.parameters[name] = Parameter( name=name, annotation=annotation, description=parsed_directive.value, default=default, kind=kind, ) return parsed_directive.next_index
def test_parse__param_field_annotate_type__param_section_with_type(): """Parse a simple docstring.""" def f(foo: str): """ Docstring with line continuation. :param foo: descriptive test text """ sections, errors = parse(f) assert len(sections) == 2 assert sections[1].type == Section.Type.PARAMETERS assert_parameter_equal( sections[1].value[0], Parameter(SOME_NAME, annotation=str, description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD), )
def test_parse__param_field_with_default__result_from_docstring(): """Parse a simple docstring.""" def f(foo=""): """ Docstring with line continuation. :param foo: descriptive test text """ sections, errors = parse(f) assert len(sections) == 2 assert sections[1].type == Section.Type.PARAMETERS assert_parameter_equal( sections[1].value[0], Parameter("foo", annotation=empty, description=SOME_TEXT, default="", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD), )
def test_parse__param_field_type_multiple__param_section_with_union(): """Parse a simple docstring.""" def f(foo): """ Docstring with line continuation. :param foo: descriptive test text :type foo: str or int or float """ sections, errors = parse(f) assert len(sections) == 2 assert sections[1].type == Section.Type.PARAMETERS assert_parameter_equal( sections[1].value[0], Parameter( SOME_NAME, annotation="Union[str,int,float]", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, ), )