def make_rst(self): env = self.state.document.settings.env path = self.arguments[0] parsed = parse_jinja_comment( os.path.join(env.config["jinja_template_path"], path)) if parsed: config = Config(napoleon_use_param=True, napoleon_use_rtype=True) for comment, macro_function in parsed: if macro_function.startswith( "{{%- macro ") or macro_function.startswith( "{{% macro "): macro_function_signature = macro_function.replace( "{{% macro ", "").replace("{{%- macro ", "").replace(" -%}}", "").replace(" %}}", "") macro_function_name = macro_function_signature.split( "(")[0] docstring = GoogleDocstring(comment, config).lines() docstring.append(macro_function_signature) if docstring is not None and env.config[ "jinja_template_path"]: for line in jinja_directive(macro_function_name, docstring): yield line yield ""
def test_attributes_with_class_reference(self): docstring = """\ Attributes: in_attr(:class:`numpy.ndarray`): super-dooper attribute """ actual = str(GoogleDocstring(docstring)) expected = """\ .. attribute:: in_attr super-dooper attribute :type: :class:`numpy.ndarray` """ self.assertEqual(expected, actual) docstring = """\ Attributes: in_attr(numpy.ndarray): super-dooper attribute """ actual = str(GoogleDocstring(docstring)) expected = """\ .. attribute:: in_attr super-dooper attribute :type: numpy.ndarray """ self.assertEqual(expected, actual)
def test_parameters_with_class_reference(self): docstring = """\ Construct a new XBlock. This class should only be used by runtimes. Arguments: runtime (:class:`~typing.Dict`\\[:class:`int`,:class:`str`\\]): Use it to access the environment. It is available in XBlock code as ``self.runtime``. field_data (:class:`FieldData`): Interface used by the XBlock fields to access their data from wherever it is persisted. scope_ids (:class:`ScopeIds`): Identifiers needed to resolve scopes. """ actual = str(GoogleDocstring(docstring)) expected = """\ Construct a new XBlock. This class should only be used by runtimes. :param runtime: Use it to access the environment. It is available in XBlock code as ``self.runtime``. :type runtime: :class:`~typing.Dict`\\[:class:`int`,:class:`str`\\] :param field_data: Interface used by the XBlock fields to access their data from wherever it is persisted. :type field_data: :class:`FieldData` :param scope_ids: Identifiers needed to resolve scopes. :type scope_ids: :class:`ScopeIds` """ self.assertEqual(expected, actual)
def test_kwargs_in_arguments(self): docstring = """Allows to create attributes binded to this device. Some other paragraph. Code sample for usage:: dev.bind(loopback=Loopback) dev.loopback.configure() Arguments: **kwargs: name/class pairs that will create resource-managers bound as instance attributes to this instance. See code example above. """ expected = """Allows to create attributes binded to this device. Some other paragraph. Code sample for usage:: dev.bind(loopback=Loopback) dev.loopback.configure() :param \\*\\*kwargs: name/class pairs that will create resource-managers bound as instance attributes to this instance. See code example above. """ actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual)
def _getparams(self, docstring: str) -> Dict[str, str]: """ Parse the given documentation string and extract the Args parameters. Returns a dictionary mapping the parameter name to the parameter text. Args: docstring: The given documentation string to extract the Args parameters from. Returns: The dictionary mapping the parameter name to the parameter text. """ if docstring is None: return {} def elementByTagName(tagname: str): return lambda n: isinstance(n, Element) and n.tagname == tagname def getvalue(node, tagname: str) -> str: return node.traverse(elementByTagName(tagname))[0].astext() def param(node): return (getvalue(node, 'field_name'), getvalue(node, 'field_body')) params = {} config = Config(napoleon_use_param=True, napoleon_use_rtype=True) document = _parse_rst(GoogleDocstring(_deindent(docstring), config).lines()) for node in document.traverse(elementByTagName('field')): name, value = param(node) for keyword in ['param', 'keyword']: if name.startswith(keyword + ' '): name = name[len(keyword):].lstrip() params[name] = value.replace('\n', ' ') return params
def test_docstrings(self): config = Config(napoleon_use_param=False, napoleon_use_rtype=False, napoleon_use_keyword=False) for docstring, expected in self.docstrings: actual = str(GoogleDocstring(dedent(docstring), config)) expected = dedent(expected) self.assertEqual(expected, actual)
def _parse_doc(func): """Extract documentation from a function's docstring.""" doc = inspect.getdoc(func) if doc is None: return _Doc('', {}) # Convert Google- or Numpy-style docstrings to RST. # (Should do nothing if not in either style.) doc = str(GoogleDocstring(doc)) doc = str(NumpyDocstring(doc)) dom = publish_doctree(doc).asdom() etree = ElementTree.fromstring(dom.toxml()) doctext = [] for element in etree: if element.tag == 'paragraph': doctext.append(_get_text(element)) elif element.tag == 'literal_block': doctext.append(_indent(_get_text(element))) doctext = '\n\n'.join(doctext) fields = etree.findall('.//field') params = defaultdict(dict) for field in fields: field_name = field.find('field_name') field_body = field.find('field_body') parts = field_name.text.split() if len(parts) == 2: doctype, name = parts elif len(parts) == 3: doctype, type_, name = parts if doctype not in _PARAM_TYPES: log.debug('ignoring field %s', field_name.text) continue log.debug('inline param type %s', type_) if 'type' in params[name]: raise ValueError('type defined twice for {}'.format(name)) params[name]['type'] = type_ else: log.debug('ignoring field %s', field_name.text) continue if doctype in _PARAM_TYPES: doctype = 'param' if doctype in _TYPE_NAMES: doctype = 'type' text = _get_text(field_body) log.debug('%s %s: %s', doctype, name, text) if doctype in params[name]: raise ValueError('{} defined twice for {}'.format(doctype, name)) params[name][doctype] = text tuples = {} for name, values in params.items(): tuples[name] = _Param(values.get('param'), values.get('type')) return _Doc(doctext, tuples)
def test_section_header_formatting(self): docstrings = [ (""" Summary line Example: Multiline reStructuredText literal code block """, """ Summary line .. rubric:: Example Multiline reStructuredText literal code block """), ################################ (""" Summary line Example:: Multiline reStructuredText literal code block """, """ Summary line Example:: Multiline reStructuredText literal code block """), ################################ (""" Summary line :Example: Multiline reStructuredText literal code block """, """ Summary line :Example: Multiline reStructuredText literal code block """) ] for docstring, expected in docstrings: actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual)
def parse_docstring(f): if f.__doc__ is None: f.__doc__ = 'TODO\n TODO' doc = inspect.cleandoc(f.__doc__) config = Config() google_doc = GoogleDocstring(doc, config) rst = str(google_doc) param_regex = r':param (?P<param>\w+): (?P<doc>.*)' m = re.findall(param_regex, rst) args_help = dict((k, v) for k, v in m) return args_help
def test_sphinx_admonitions(self): admonition_map = { 'Attention': 'attention', 'Caution': 'caution', 'Danger': 'danger', 'Error': 'error', 'Hint': 'hint', 'Important': 'important', 'Note': 'note', 'Tip': 'tip', 'Todo': 'todo', 'Warning': 'warning', 'Warnings': 'warning', } config = Config() for section, admonition in admonition_map.items(): # Multiline actual = str( GoogleDocstring( ("{}:\n" " this is the first line\n" "\n" " and this is the second line\n").format(section), config)) expect = (".. {}::\n" "\n" " this is the first line\n" " \n" " and this is the second line\n").format(admonition) self.assertEqual(expect, actual) # Single line actual = str( GoogleDocstring( ("{}:\n" " this is a single line\n").format(section), config)) expect = (".. {}:: this is a single line\n").format(admonition) self.assertEqual(expect, actual)
def parse_header(f): if f.__doc__ is None: f.__doc__ = 'TODO\n TODO' doc = inspect.cleandoc(f.__doc__) config = Config() google_doc = GoogleDocstring(doc, config) rst = str(google_doc) lines = [l for l in rst.splitlines() if len(l) > 0] if len(lines) >= 2: return lines[:2] elif len(lines) == 1: return lines[0] else: return None, None
def parse_docstr(s, level=1): """Parse docstr s and indent it with level spaces""" lines = GoogleDocstring(s, napoleon_config).lines() if isinstance(lines, str): # Napoleon failed s = s.split('\n') while s and s[0] == '': s = s[1:] while s and s[-1] == '': s = s[:-1] if not s: return '' i = 0 indent = len(list(itertools.takewhile(lambda i: i == ' ', s[0]))) lines = [l[indent:] for l in s] return '\n'.join((' ' * level) + l for l in lines)
def test_class_data_member_inline(self): config = Config() docstring = """b: data member description with :ref:`reference`""" actual = str( GoogleDocstring(docstring, config=config, app=None, what='attribute', name='some_data', obj=0)) expected = """data member description with :ref:`reference` :type: b""" self.assertEqual(expected, actual)
def test_keywords_with_types(self): docstring = """\ Do as you please Keyword Args: gotham_is_yours (None): shall interfere. """ actual = str(GoogleDocstring(docstring)) expected = """\ Do as you please :keyword gotham_is_yours: shall interfere. :kwtype gotham_is_yours: None """ self.assertEqual(expected, actual)
def test_xrefs_in_return_type(self): docstring = """Example Function Returns: :class:`numpy.ndarray`: A :math:`n \\times 2` array containing a bunch of math items """ expected = """Example Function :returns: A :math:`n \\times 2` array containing a bunch of math items :rtype: :class:`numpy.ndarray` """ actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual)
def test_colon_in_return_type(self): docstring = """Example property. Returns: :py:class:`~.module.submodule.SomeClass`: an example instance if available, None if not available. """ expected = """Example property. :returns: an example instance if available, None if not available. :rtype: :py:class:`~.module.submodule.SomeClass` """ actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual)
def _process_docstring(app, what, name, obj, options, lines): # type: (Sphinx, unicode, unicode, Any, Any, List[unicode]) -> None """Process the docstring for a given python object. Called when autodoc has read and processed a docstring. `lines` is a list of docstring lines that `_process_docstring` modifies in place to change what Sphinx outputs. The following settings in conf.py control what styles of docstrings will be parsed: * ``napoleon_google_docstring`` -- parse Google style docstrings * ``napoleon_numpy_docstring`` -- parse NumPy style docstrings Parameters ---------- app : sphinx.application.Sphinx Application object representing the Sphinx process. what : str A string specifying the type of the object to which the docstring belongs. Valid values: "module", "class", "exception", "function", "method", "attribute". name : str The fully qualified name of the object. obj : module, class, exception, function, method, or attribute The object to which the docstring belongs. options : sphinx.ext.autodoc.Options The options given to the directive: an object with attributes inherited_members, undoc_members, show_inheritance and noindex that are True if the flag option of same name was given to the auto directive. lines : list of str The lines of the docstring, see above. .. note:: `lines` is modified *in place* """ result_lines = lines docstring = None # type: GoogleDocstring if app.config.napoleon_numpy_docstring: docstring = NumpyDocstring(result_lines, app.config, app, what, name, obj, options) result_lines = docstring.lines() if app.config.napoleon_google_docstring: docstring = GoogleDocstring(result_lines, app.config, app, what, name, obj, options) result_lines = docstring.lines() lines[:] = result_lines[:]
def test_code_block_in_returns_section(self): docstring = """ Returns: foobar: foo:: codecode codecode """ expected = """ :returns: foo:: codecode codecode :rtype: foobar """ actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual)
def test_class_data_member(self): config = Config() docstring = """data member description: - a: b """ actual = str( GoogleDocstring(docstring, config=config, app=None, what='attribute', name='some_data', obj=0)) expected = """data member description: - a: b""" self.assertEqual(expected, actual)
def test_custom_generic_sections(self): docstrings = (("""\ Really Important Details: You should listen to me! """, """.. rubric:: Really Important Details You should listen to me! """), ("""\ Sooper Warning: Stop hitting yourself! """, """:Warns: **Stop hitting yourself!** """)) testConfig = Config(napoleon_custom_sections=[ 'Really Important Details', ('Sooper Warning', 'warns') ]) for docstring, expected in docstrings: actual = str(GoogleDocstring(docstring, testConfig)) self.assertEqual(expected, actual)
def test_list_in_parameter_description(self): docstring = """One line summary. Parameters: no_list (int): one_bullet_empty (int): * one_bullet_single_line (int): - first line one_bullet_two_lines (int): + first line continued two_bullets_single_line (int): - first line - second line two_bullets_two_lines (int): * first line continued * second line continued one_enumeration_single_line (int): 1. first line one_enumeration_two_lines (int): 1) first line continued two_enumerations_one_line (int): (iii) first line (iv) second line two_enumerations_two_lines (int): a. first line continued b. second line continued one_definition_one_line (int): item 1 first line one_definition_two_lines (int): item 1 first line continued two_definitions_one_line (int): item 1 first line item 2 second line two_definitions_two_lines (int): item 1 first line continued item 2 second line continued one_definition_blank_line (int): item 1 first line extra first line two_definitions_blank_lines (int): item 1 first line extra first line item 2 second line extra second line definition_after_inline_text (int): text line item 1 first line definition_after_normal_text (int): text line item 1 first line """ expected = """One line summary. :param no_list: :type no_list: int :param one_bullet_empty: * :type one_bullet_empty: int :param one_bullet_single_line: - first line :type one_bullet_single_line: int :param one_bullet_two_lines: + first line continued :type one_bullet_two_lines: int :param two_bullets_single_line: - first line - second line :type two_bullets_single_line: int :param two_bullets_two_lines: * first line continued * second line continued :type two_bullets_two_lines: int :param one_enumeration_single_line: 1. first line :type one_enumeration_single_line: int :param one_enumeration_two_lines: 1) first line continued :type one_enumeration_two_lines: int :param two_enumerations_one_line: (iii) first line (iv) second line :type two_enumerations_one_line: int :param two_enumerations_two_lines: a. first line continued b. second line continued :type two_enumerations_two_lines: int :param one_definition_one_line: item 1 first line :type one_definition_one_line: int :param one_definition_two_lines: item 1 first line continued :type one_definition_two_lines: int :param two_definitions_one_line: item 1 first line item 2 second line :type two_definitions_one_line: int :param two_definitions_two_lines: item 1 first line continued item 2 second line continued :type two_definitions_two_lines: int :param one_definition_blank_line: item 1 first line extra first line :type one_definition_blank_line: int :param two_definitions_blank_lines: item 1 first line extra first line item 2 second line extra second line :type two_definitions_blank_lines: int :param definition_after_inline_text: text line item 1 first line :type definition_after_inline_text: int :param definition_after_normal_text: text line item 1 first line :type definition_after_normal_text: int """ config = Config(napoleon_use_param=True) actual = str(GoogleDocstring(docstring, config)) self.assertEqual(expected, actual) expected = """One line summary. :Parameters: * **no_list** (*int*) * **one_bullet_empty** (*int*) -- * * **one_bullet_single_line** (*int*) -- - first line * **one_bullet_two_lines** (*int*) -- + first line continued * **two_bullets_single_line** (*int*) -- - first line - second line * **two_bullets_two_lines** (*int*) -- * first line continued * second line continued * **one_enumeration_single_line** (*int*) -- 1. first line * **one_enumeration_two_lines** (*int*) -- 1) first line continued * **two_enumerations_one_line** (*int*) -- (iii) first line (iv) second line * **two_enumerations_two_lines** (*int*) -- a. first line continued b. second line continued * **one_definition_one_line** (*int*) -- item 1 first line * **one_definition_two_lines** (*int*) -- item 1 first line continued * **two_definitions_one_line** (*int*) -- item 1 first line item 2 second line * **two_definitions_two_lines** (*int*) -- item 1 first line continued item 2 second line continued * **one_definition_blank_line** (*int*) -- item 1 first line extra first line * **two_definitions_blank_lines** (*int*) -- item 1 first line extra first line item 2 second line extra second line * **definition_after_inline_text** (*int*) -- text line item 1 first line * **definition_after_normal_text** (*int*) -- text line item 1 first line """ config = Config(napoleon_use_param=False) actual = str(GoogleDocstring(docstring, config)) self.assertEqual(expected, actual)
def test_docstrings(self): for docstring, expected in self.docstrings: actual = str(GoogleDocstring(textwrap.dedent(docstring))) expected = textwrap.dedent(expected) self.assertEqual(expected, actual)
def test_raises_types(self): docstrings = [ (""" Example Function Raises: RuntimeError: A setting wasn't specified, or was invalid. ValueError: Something something value error. """, """ Example Function :raises RuntimeError: A setting wasn't specified, or was invalid. :raises ValueError: Something something value error. """), ################################ (""" Example Function Raises: InvalidDimensionsError """, """ Example Function :raises InvalidDimensionsError: """), ################################ (""" Example Function Raises: Invalid Dimensions Error """, """ Example Function :raises Invalid Dimensions Error: """), ################################ (""" Example Function Raises: Invalid Dimensions Error: With description """, """ Example Function :raises Invalid Dimensions Error: With description """), ################################ (""" Example Function Raises: InvalidDimensionsError: If the dimensions couldn't be parsed. """, """ Example Function :raises InvalidDimensionsError: If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises: Invalid Dimensions Error: If the dimensions couldn't be parsed. """, """ Example Function :raises Invalid Dimensions Error: If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises: If the dimensions couldn't be parsed. """, """ Example Function :raises If the dimensions couldn't be parsed.: """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError` """, """ Example Function :raises exc.InvalidDimensionsError: """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed. """, """ Example Function :raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed, then a :class:`exc.InvalidDimensionsError` will be raised. """, """ Example Function :raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed, then a :class:`exc.InvalidDimensionsError` will be raised. """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed. :class:`exc.InvalidArgumentsError`: If the arguments are invalid. """, """ Example Function :raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed. :raises exc.InvalidArgumentsError: If the arguments are invalid. """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError` :class:`exc.InvalidArgumentsError` """, """ Example Function :raises exc.InvalidDimensionsError: :raises exc.InvalidArgumentsError: """) ] for docstring, expected in docstrings: actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual)
def test_raises_types(self): docstrings = [ (""" Example Function Raises: InvalidDimensionsError """, """ Example Function :raises: :exc:`InvalidDimensionsError` """), ################################ (""" Example Function Raises: Invalid Dimensions Error """, """ Example Function :raises: Invalid Dimensions Error """), ################################ (""" Example Function Raises: Invalid Dimensions Error: With description """, """ Example Function :raises: *Invalid Dimensions Error* -- With description """), ################################ (""" Example Function Raises: InvalidDimensionsError: If the dimensions couldn't be parsed. """, """ Example Function :raises: :exc:`InvalidDimensionsError` -- If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises: Invalid Dimensions Error: If the dimensions couldn't be parsed. """, """ Example Function :raises: *Invalid Dimensions Error* -- If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises: If the dimensions couldn't be parsed. """, """ Example Function :raises: If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError` """, """ Example Function :raises: :class:`exc.InvalidDimensionsError` """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed. """, """ Example Function :raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't be parsed. """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed, then a :class:`exc.InvalidDimensionsError` will be raised. """, """ Example Function :raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't be parsed, then a :class:`exc.InvalidDimensionsError` will be raised. """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed. :class:`exc.InvalidArgumentsError`: If the arguments are invalid. """, """ Example Function :raises: * :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't be parsed. * :class:`exc.InvalidArgumentsError` -- If the arguments are invalid. """), ################################ (""" Example Function Raises: :class:`exc.InvalidDimensionsError` :class:`exc.InvalidArgumentsError` """, """ Example Function :raises: * :class:`exc.InvalidDimensionsError` * :class:`exc.InvalidArgumentsError` """) ] for docstring, expected in docstrings: actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual)
def _parse_docstring(doc): """Extract documentation from a function's docstring.""" _cache_key = doc try: return _parse_docstring_cache[_cache_key] except KeyError: pass if doc is None: return _Doc('', '', {}) # Convert Google- or Numpy-style docstrings to RST. # (Should do nothing if not in either style.) doc = str(GoogleDocstring(doc)) doc = str(NumpyDocstring(doc)) tree = publish_doctree(doc) class Visitor(NodeVisitor): optional = [ 'document', 'docinfo', 'field_list', 'field_body', 'literal', 'problematic'] def __init__(self, document): NodeVisitor.__init__(self, document) self.paragraphs = [] self.start_lines = [] self.params = defaultdict(dict) self._current_paragraph = None self._indent_iterator_stack = [] self._indent_stack = [] def _do_nothing(self, node): pass def visit_paragraph(self, node): self.start_lines.append(node.line) self._current_paragraph = [] def depart_paragraph(self, node): text = ''.join(self._current_paragraph) text = ''.join(self._indent_stack) + text self._indent_stack = [ ' ' * len(item) for item in self._indent_stack] text = text.replace('\n', '\n' + ''.join(self._indent_stack)) self.paragraphs.append(text) self._current_paragraph = None def visit_Text(self, node): self._current_paragraph.append(node) depart_Text = _do_nothing def visit_emphasis(self, node): self._current_paragraph.append('\033[3m') # *foo*: italic def visit_strong(self, node): self._current_paragraph.append('\033[1m') # **foo**: bold def visit_title_reference(self, node): self._current_paragraph.append('\033[4m') # `foo`: underlined def _depart_markup(self, node): self._current_paragraph.append('\033[0m') depart_emphasis = depart_strong = depart_title_reference = \ _depart_markup def visit_literal_block(self, node): text, = node self.start_lines.append(node.line) self.paragraphs.append(re.sub('^|\n', r'\g<0> ', text)) # indent raise SkipNode def visit_bullet_list(self, node): self._indent_iterator_stack.append( (node['bullet'] + ' ' for _ in range(len(node)))) def depart_bullet_list(self, node): self._indent_iterator_stack.pop() def visit_enumerated_list(self, node): enumtype = node['enumtype'] fmt = {('(', ')'): 'parens', ('', ')'): 'rparen', ('', '.'): 'period'}[node['prefix'], node['suffix']] try: start = node['start'] except KeyError: start = 1 else: start = { 'arabic': int, 'loweralpha': lambda s: ord(s) - ord('a') + 1, 'upperalpha': lambda s: ord(s) - ord('A') + 1, 'lowerroman': lambda s: roman.fromRoman(s.upper()), 'upperroman': lambda s: roman.fromRoman(s), }[enumtype](start) enumerators = [Body(None).make_enumerator(i, enumtype, fmt)[0] for i in range(start, start + len(node))] width = max(map(len, enumerators)) enumerators = [enum.ljust(width) for enum in enumerators] self._indent_iterator_stack.append(iter(enumerators)) def depart_enumerated_list(self, node): self._indent_iterator_stack.pop() def visit_list_item(self, node): self._indent_stack.append(next(self._indent_iterator_stack[-1])) def depart_list_item(self, node): self._indent_stack.pop() def visit_field(self, node): field_name_node, field_body_node = node field_name, = field_name_node parts = field_name.split() if len(parts) == 2: doctype, name = parts elif len(parts) == 3: doctype, type_, name = parts if doctype not in _PARAM_TYPES: raise SkipNode if 'type' in self.params[name]: raise ValueError('type defined twice for {}'.format(name)) self.params[name]['type'] = type_ else: raise SkipNode if doctype in _PARAM_TYPES: doctype = 'param' if doctype in _TYPE_NAMES: doctype = 'type' if doctype in self.params[name]: raise ValueError( '{} defined twice for {}'.format(doctype, name)) visitor = Visitor(self.document) field_body_node.walkabout(visitor) self.params[name][doctype] = ''.join(visitor.paragraphs) raise SkipNode def visit_comment(self, node): raise SkipNode def visit_system_message(self, node): raise SkipNode visitor = Visitor(tree) tree.walkabout(visitor) tuples = {name: _Param(values.get('param'), values.get('type')) for name, values in visitor.params.items()} if visitor.paragraphs: text = [] for start, paragraph, next_start in zip( visitor.start_lines, visitor.paragraphs, visitor.start_lines[1:] + [0]): text.append(paragraph) # We insert a space before each newline to prevent argparse # from stripping consecutive newlines down to just two # (http://bugs.python.org/issue31330). text.append(' \n' * (next_start - start - paragraph.count('\n'))) parsed = _Doc('', ''.join(text), tuples) else: parsed = _Doc('', '', tuples) _parse_docstring_cache[_cache_key] = parsed return parsed
def main(text=None): src = sys.stdin.read() if text is None else text rest_formatter.main(str(GoogleDocstring(textwrap.dedent(src))))
def convert_docstring(docstring): if is_google_style(docstring): return str(GoogleDocstring(docstring, CONFIG)) elif is_numpy_style(docstring): return str(NumpyDocstring(docstring, CONFIG)) return docstring