From b0d494ff14f5ea50040035f6e0896e236158b485 Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sun, 9 Jun 2019 15:51:45 +0200 Subject: [PATCH 1/5] Run black. --- docinstance/__init__.py | 2 +- docinstance/content/description.py | 201 ++++--- docinstance/content/equation.py | 37 +- docinstance/content/section.py | 181 ++++--- docinstance/content/test/test_base.py | 9 +- docinstance/content/test/test_description.py | 243 +++++---- docinstance/content/test/test_equation.py | 57 +- docinstance/content/test/test_section.py | 414 ++++++++------ docinstance/docstring.py | 124 +++-- docinstance/parser/latex.py | 10 +- docinstance/parser/numpy.py | 161 +++--- docinstance/parser/test/test_latex.py | 38 +- docinstance/parser/test/test_numpy.py | 539 ++++++++++++------- docinstance/test/test_docstring.py | 187 ++++--- docinstance/test/test_utils.py | 120 +++-- docinstance/test/test_wrapper.py | 310 ++++++----- docinstance/utils.py | 39 +- docinstance/wrapper.py | 27 +- 18 files changed, 1597 insertions(+), 1102 deletions(-) diff --git a/docinstance/__init__.py b/docinstance/__init__.py index a0b5a7e..12517ef 100644 --- a/docinstance/__init__.py +++ b/docinstance/__init__.py @@ -1,3 +1,3 @@ """Module for representing a docstring as an instance of a Docstring class.""" # pylint: disable=C0103 -name = 'docinstance' +name = "docinstance" diff --git a/docinstance/content/description.py b/docinstance/content/description.py index 3d9de03..2744270 100644 --- a/docinstance/content/description.py +++ b/docinstance/content/description.py @@ -43,7 +43,7 @@ class DocDescription(DocContent): """ - def __init__(self, name, signature='', types=None, descs=None): + def __init__(self, name, signature="", types=None, descs=None): """Initialize the object. Parameters @@ -86,20 +86,27 @@ def __init__(self, name, signature='', types=None, descs=None): types = [] elif isinstance(types, (type, str)): types = [types] - elif not (isinstance(types, (list, tuple)) and - all(isinstance(i, (type, str)) for i in types)): - raise TypeError("Types of allowed objects must be given as a class or list/tuple of " - "classes/strings.") + elif not ( + isinstance(types, (list, tuple)) and all(isinstance(i, (type, str)) for i in types) + ): + raise TypeError( + "Types of allowed objects must be given as a class or list/tuple of " + "classes/strings." + ) self.types = list(types) if descs is None: descs = [] elif isinstance(descs, (str, DocEquation)): descs = [descs] - elif not (isinstance(descs, (list, tuple)) and - all(isinstance(i, (str, DocEquation)) for i in descs)): - raise TypeError("Descriptions of the object/error must be given as a string or " - "list/tuple of strings") + elif not ( + isinstance(descs, (list, tuple)) + and all(isinstance(i, (str, DocEquation)) for i in descs) + ): + raise TypeError( + "Descriptions of the object/error must be given as a string or " + "list/tuple of strings" + ) self.descs = list(descs) @property @@ -143,11 +150,13 @@ def make_numpy_docstring(self, width, indent_level, tabsize): The signature of a function is not included in the numpy docstring. """ - if self.signature != '': - print('Warning: In NumPy docstring format, the signature of a function is not ' - 'included.') + if self.signature != "": + print( + "Warning: In NumPy docstring format, the signature of a function is not " + "included." + ) - output = '' + output = "" # var_name # OR # error_name @@ -157,39 +166,53 @@ def make_numpy_docstring(self, width, indent_level, tabsize): output += wrap(self.name, width=width, indent_level=indent_level, tabsize=tabsize)[0] # var_name : var_type elif len(self.types) == 1: - name_type = wrap('{0} : {1}'.format(self.name, self.types_str[0]), - width=width, indent_level=indent_level, tabsize=tabsize) + name_type = wrap( + "{0} : {1}".format(self.name, self.types_str[0]), + width=width, + indent_level=indent_level, + tabsize=tabsize, + ) # check that the both the name and the type can fit in the given width and indentation if len(name_type) > 1: # FIXME: need a better message - raise ValueError('The name and the type of the variable are too long to fit into ' - 'given width and indentation.') + raise ValueError( + "The name and the type of the variable are too long to fit into " + "given width and indentation." + ) output += name_type[0] # var_name : {var_type1, var_type2, default_type} else: - name_types = wrap('{0} : {{{1}}}'.format(self.name, ', '.join(self.types_str)), - width=width, indent_level=indent_level, tabsize=tabsize) + name_types = wrap( + "{0} : {{{1}}}".format(self.name, ", ".join(self.types_str)), + width=width, + indent_level=indent_level, + tabsize=tabsize, + ) # if there are too many types to fit into one line, the remaining lines should be # indented to line up after "var_name : {" - wrap_point = len('{0} : {{'.format(self.name)) + wrap_point = len("{0} : {{".format(self.name)) # check that the name and first type can fit into the first line - if not name_types[0].startswith('{0}{1} : {{{2}'.format(' ' * indent_level * tabsize, - self.name, self.types_str[0])): + if not name_types[0].startswith( + "{0}{1} : {{{2}".format(" " * indent_level * tabsize, self.name, self.types_str[0]) + ): # FIXME: need a better message - raise ValueError('The name and the first type of the variable are too long to fit ' - 'into the given width and indentation.') + raise ValueError( + "The name and the first type of the variable are too long to fit " + "into the given width and indentation." + ) # wrap the remaining lines name_types_lines = [name_types[0]] for line in name_types[1:]: name_types_lines += wrap(line, width=width, indent_level=1, tabsize=wrap_point) - output += '\n'.join(name_types_lines) - output += '\n' + output += "\n".join(name_types_lines) + output += "\n" # descriptions for paragraph in self.descs: - output += '\n'.join(wrap(paragraph, - width=width, indent_level=indent_level+1, tabsize=tabsize)) - output += '\n' + output += "\n".join( + wrap(paragraph, width=width, indent_level=indent_level + 1, tabsize=tabsize) + ) + output += "\n" return output @@ -212,8 +235,8 @@ def make_numpy_docstring_signature(self, width, indent_level, tabsize): signature. """ - new_name = '{0}{1}'.format(self.name, self.signature) - new_description = self.__class__(new_name, '', self.types, self.descs) + new_name = "{0}{1}".format(self.name, self.signature) + new_description = self.__class__(new_name, "", self.types, self.descs) return new_description.make_numpy_docstring(width, indent_level, tabsize) def make_google_docstring(self, width, indent_level, tabsize): @@ -238,42 +261,55 @@ def make_google_docstring(self, width, indent_level, tabsize): The signature of a function is not included in the google docstring. """ - output = '' + output = "" first_block = [] # var_name: if not self.types: - first_block = wrap('{0}:'.format(self.name), - width=width, indent_level=indent_level, tabsize=tabsize) + first_block = wrap( + "{0}:".format(self.name), width=width, indent_level=indent_level, tabsize=tabsize + ) # var_name (type1, type2): else: # FIXME: optional parameters need to be specified within google doc - types_str = [i if isinstance(i, str) else ':obj:`{0}`'.format(j) - for i, j in zip(self.types, self.types_str)] - text = '{0} ({1}):'.format(self.name, ', '.join(types_str)) + types_str = [ + i if isinstance(i, str) else ":obj:`{0}`".format(j) + for i, j in zip(self.types, self.types_str) + ] + text = "{0} ({1}):".format(self.name, ", ".join(types_str)) # if there are too many types to fit into one line, the remaining lines should be # indented to line up after "var_name (" # FIXME: following can probably be replaced with a better wrapping function - first_block = [' ' * indent_level * tabsize + line for line in - wrap_indent_subsequent(text, width=width - indent_level*tabsize, - indent_level=1, - tabsize=len('{0} ('.format(self.name)))] + first_block = [ + " " * indent_level * tabsize + line + for line in wrap_indent_subsequent( + text, + width=width - indent_level * tabsize, + indent_level=1, + tabsize=len("{0} (".format(self.name)), + ) + ] # add descriptions if self.descs: - output += '\n'.join(first_block[:-1]) + output += "\n".join(first_block[:-1]) if len(first_block) != 1: - output += '\n' + output += "\n" # FIXME: following can probably be replaced with a better wrapping function - first_desc = wrap_indent_subsequent(first_block[-1] + ' ' + self.descs[0], width=width, - indent_level=indent_level+1, tabsize=tabsize) - output += '\n'.join(first_desc) - output += '\n' + first_desc = wrap_indent_subsequent( + first_block[-1] + " " + self.descs[0], + width=width, + indent_level=indent_level + 1, + tabsize=tabsize, + ) + output += "\n".join(first_desc) + output += "\n" for desc in self.descs[1:]: - output += '\n'.join(wrap(desc, width=width, indent_level=indent_level+1, - tabsize=tabsize)) - output += '\n' + output += "\n".join( + wrap(desc, width=width, indent_level=indent_level + 1, tabsize=tabsize) + ) + output += "\n" else: - output += '\n'.join(first_block) - output += '\n' + output += "\n".join(first_block) + output += "\n" return output def make_rst_docstring(self, width, indent_level, tabsize): @@ -299,36 +335,51 @@ def make_rst_docstring(self, width, indent_level, tabsize): If the parameter name is too long to fit within the given width and indent. """ - output = '' + output = "" if self.descs: - text = ':param {0}: {1}'.format(self.name, self.descs[0]) + text = ":param {0}: {1}".format(self.name, self.descs[0]) # FIXME: following can probably be replaced with a better wrapping function - block = [' ' * indent_level * tabsize + line for line in - wrap_indent_subsequent(text, width=width - indent_level*tabsize, - indent_level=1, tabsize=tabsize)] - output += '\n'.join(block) - output += '\n' + block = [ + " " * indent_level * tabsize + line + for line in wrap_indent_subsequent( + text, width=width - indent_level * tabsize, indent_level=1, tabsize=tabsize + ) + ] + output += "\n".join(block) + output += "\n" if len(self.descs) > 1: for desc in self.descs[1:]: - output += '\n'.join(wrap(desc, width=width, indent_level=indent_level+1, - tabsize=tabsize)) - output += '\n' + output += "\n".join( + wrap(desc, width=width, indent_level=indent_level + 1, tabsize=tabsize) + ) + output += "\n" else: - block = wrap(':param {0}:'.format(self.name), width=width, indent_level=indent_level, - tabsize=tabsize) + block = wrap( + ":param {0}:".format(self.name), + width=width, + indent_level=indent_level, + tabsize=tabsize, + ) if len(block) > 1: - raise ValueError('Parameter name is too long to fit within the given line width and' - ' indent level.') + raise ValueError( + "Parameter name is too long to fit within the given line width and" + " indent level." + ) output += block[0] - output += '\n' + output += "\n" - types_str = [i if isinstance(i, str) else ':obj:`{0}`'.format(j) - for i, j in zip(self.types, self.types_str)] + types_str = [ + i if isinstance(i, str) else ":obj:`{0}`".format(j) + for i, j in zip(self.types, self.types_str) + ] if self.types: - text = ':type {0}: {1}'.format(self.name, ', '.join(types_str)) - block = [' ' * indent_level * tabsize + line for line in - wrap_indent_subsequent(text, width=width - indent_level*tabsize, - indent_level=1, tabsize=tabsize)] - output += '\n'.join(block) - output += '\n' + text = ":type {0}: {1}".format(self.name, ", ".join(types_str)) + block = [ + " " * indent_level * tabsize + line + for line in wrap_indent_subsequent( + text, width=width - indent_level * tabsize, indent_level=1, tabsize=tabsize + ) + ] + output += "\n".join(block) + output += "\n" return output diff --git a/docinstance/content/equation.py b/docinstance/content/equation.py index d96eed8..5a0e3a6 100644 --- a/docinstance/content/equation.py +++ b/docinstance/content/equation.py @@ -35,9 +35,9 @@ def __init__(self, equations): """ if not isinstance(equations, str): - raise TypeError('Equations must be given as one string.') - self.equations = equations.split('\n') - if self.equations[-1] == '': + raise TypeError("Equations must be given as one string.") + self.equations = equations.split("\n") + if self.equations[-1] == "": self.equations = self.equations[:-1] def make_numpy_docstring(self, width, indent_level, tabsize): @@ -63,22 +63,29 @@ def make_numpy_docstring(self, width, indent_level, tabsize): If the width is too small to fit the equation for the given indent and tabsize. """ - output = '' + output = "" if len(self.equations) == 1: - first_line = wrap('.. math:: ' + self.equations[0], - width=width, indent_level=indent_level, tabsize=tabsize) + first_line = wrap( + ".. math:: " + self.equations[0], + width=width, + indent_level=indent_level, + tabsize=tabsize, + ) if len(first_line) == 1: output += first_line[0] - output += '\n\n' + output += "\n\n" return output - first_line = wrap('.. math:: ', width=width, indent_level=indent_level, tabsize=tabsize) + first_line = wrap(".. math:: ", width=width, indent_level=indent_level, tabsize=tabsize) if len(first_line) != 1: - raise ValueError('Given line width is too small to fit the equation for the given ' - 'indent and tab size') + raise ValueError( + "Given line width is too small to fit the equation for the given " + "indent and tab size" + ) output += first_line[0] - output += '\n\n' - output += '\n'.join('\n'.join(wrap(equation, width=width, indent_level=indent_level + 1, - tabsize=tabsize)) - for equation in self.equations) - output += '\n\n' + output += "\n\n" + output += "\n".join( + "\n".join(wrap(equation, width=width, indent_level=indent_level + 1, tabsize=tabsize)) + for equation in self.equations + ) + output += "\n\n" return output diff --git a/docinstance/content/section.py b/docinstance/content/section.py index dc8c177..7a44571 100644 --- a/docinstance/content/section.py +++ b/docinstance/content/section.py @@ -57,13 +57,22 @@ def __init__(self, header, contents): contents = [contents] # NOTE: is it really necessary to prevent contents of a section from mixing # strings/DocContent and DocDescription? - elif not (isinstance(contents, (tuple, list)) and - (all(isinstance(content, (str, DocContent)) and - not isinstance(content, DocDescription) for content in contents) or - all(isinstance(content, DocDescription) for content in contents))): - raise TypeError("The parameter `contents` must be a string, a list/tuple of " - "DocDescription, or a list/tuple of strings/DocContent (not " - "DocDescription).") + elif not ( + isinstance(contents, (tuple, list)) + and ( + all( + isinstance(content, (str, DocContent)) + and not isinstance(content, DocDescription) + for content in contents + ) + or all(isinstance(content, DocDescription) for content in contents) + ) + ): + raise TypeError( + "The parameter `contents` must be a string, a list/tuple of " + "DocDescription, or a list/tuple of strings/DocContent (not " + "DocDescription)." + ) self.contents = list(contents) # pylint: disable=W0221 @@ -94,27 +103,30 @@ def make_numpy_docstring(self, width, indent_level, tabsize, include_signature=F If the title is too long for the given width and indentation. """ - output = '' + output = "" # title - if self.header != '': - title = wrap(self.header.title(), width=width, indent_level=indent_level, - tabsize=tabsize) - divider = wrap('-' * len(self.header), width=width, indent_level=indent_level, - tabsize=tabsize) + if self.header != "": + title = wrap( + self.header.title(), width=width, indent_level=indent_level, tabsize=tabsize + ) + divider = wrap( + "-" * len(self.header), width=width, indent_level=indent_level, tabsize=tabsize + ) # NOTE: error will be raised if the line width is not wide enough to fit the title and # the divider in one line because the divider is one word and wrap will complain if the # line width is not big enough to fit the first word - output += '{0}\n{1}\n'.format(title[0], divider[0]) + output += "{0}\n{1}\n".format(title[0], divider[0]) # contents for paragraph in self.contents: # NOTE: since the contents are checked in the initialization, we will assume that the # paragraph can only be string or DocDescription if isinstance(paragraph, str): - output += '\n'.join(wrap(paragraph, width=width, indent_level=indent_level, - tabsize=tabsize)) - output += '\n\n' + output += "\n".join( + wrap(paragraph, width=width, indent_level=indent_level, tabsize=tabsize) + ) + output += "\n\n" # if isinstance(paragraph, DocContent) - elif include_signature and hasattr(paragraph, 'make_numpy_docstring_signature'): + elif include_signature and hasattr(paragraph, "make_numpy_docstring_signature"): output += paragraph.make_numpy_docstring_signature(width, indent_level, tabsize) else: output += paragraph.make_numpy_docstring(width, indent_level, tabsize) @@ -123,7 +135,7 @@ def make_numpy_docstring(self, width, indent_level, tabsize, include_signature=F else: # end a section with two newlines (note that the section already ends with a newline if # it ends with a paragraph) - output += '\n' * isinstance(paragraph, DocDescription) + output += "\n" * isinstance(paragraph, DocDescription) return output @@ -176,15 +188,19 @@ def make_google_docstring(self, width, indent_level, tabsize): If the title is too long for the given width and indentation. """ - output = '' + output = "" # title - if self.header != '': - title = wrap('{0}:'.format(self.header.title()), - width=width, indent_level=indent_level, tabsize=tabsize) + if self.header != "": + title = wrap( + "{0}:".format(self.header.title()), + width=width, + indent_level=indent_level, + tabsize=tabsize, + ) if len(title) > 1: - raise ValueError('The header must fit into the given width with the indentation') + raise ValueError("The header must fit into the given width with the indentation") output += title[0] - output += '\n' + output += "\n" else: # don't indent the contents if there is no header indent_level -= 1 @@ -193,18 +209,19 @@ def make_google_docstring(self, width, indent_level, tabsize): # NOTE: since the contents are checked in the initialization, we will assume that the # paragraph can only be string or DocDescription if isinstance(paragraph, str): - output += '\n'.join(wrap(paragraph, width=width, indent_level=indent_level+1, - tabsize=tabsize)) - output += '\n\n' + output += "\n".join( + wrap(paragraph, width=width, indent_level=indent_level + 1, tabsize=tabsize) + ) + output += "\n\n" # if isinstance(paragraph, DocContent) else: - output += paragraph.make_google_docstring(width, indent_level+1, tabsize) + output += paragraph.make_google_docstring(width, indent_level + 1, tabsize) # pylint: disable=W0120 # following block clause should always be executed else: # end a section with two newlines (note that the section already ends with a newline if # it ends with a paragraph) - output += '\n' * isinstance(paragraph, DocDescription) + output += "\n" * isinstance(paragraph, DocDescription) return output @@ -226,45 +243,57 @@ def make_rst_docstring(self, width, indent_level, tabsize): Docstring of the given content in sphinx's rst style. """ - output = '' - header = '' - special_headers = {'see also': 'seealso', 'warnings': 'warning', 'warning': 'warning', - 'notes': 'note', 'note': 'note', 'to do': 'todo', 'todo': 'todo'} + output = "" + header = "" + special_headers = { + "see also": "seealso", + "warnings": "warning", + "warning": "warning", + "notes": "note", + "note": "note", + "to do": "todo", + "todo": "todo", + } if self.header.lower() in special_headers: - header = '.. {0}::'.format(special_headers[self.header.lower()]) - elif self.header != '': - output += ':{0}:\n\n'.format(self.header.title()) + header = ".. {0}::".format(special_headers[self.header.lower()]) + elif self.header != "": + output += ":{0}:\n\n".format(self.header.title()) for i, paragraph in enumerate(self.contents): # first content must be treated with care for special headers if i == 0 and self.header.lower() in special_headers: # str if isinstance(paragraph, str): - text = '{0} {1}'.format(header, paragraph) + text = "{0} {1}".format(header, paragraph) # FIXME: following can probably be replaced with a better wrapping function - first_content = [' ' * indent_level * tabsize + line for line in - wrap_indent_subsequent(text, - width=width - indent_level*tabsize, - indent_level=indent_level+1, - tabsize=tabsize)] - output += '\n'.join(first_content) - output += '\n' + first_content = [ + " " * indent_level * tabsize + line + for line in wrap_indent_subsequent( + text, + width=width - indent_level * tabsize, + indent_level=indent_level + 1, + tabsize=tabsize, + ) + ] + output += "\n".join(first_content) + output += "\n" # DocContent else: output += header - output += '\n' - output += paragraph.make_rst_docstring(width=width, - indent_level=indent_level+1, - tabsize=tabsize) + output += "\n" + output += paragraph.make_rst_docstring( + width=width, indent_level=indent_level + 1, tabsize=tabsize + ) # indent all susequent content indent_level += 1 elif isinstance(paragraph, str): - output += '\n'.join(wrap(paragraph, width=width, indent_level=indent_level, - tabsize=tabsize)) + output += "\n".join( + wrap(paragraph, width=width, indent_level=indent_level, tabsize=tabsize) + ) # NOTE: the second newline may cause problems (because the field might not # recognize text that is more than one newline away) - output += '\n\n' + output += "\n\n" else: output += paragraph.make_rst_docstring(width, indent_level, tabsize) # pylint: disable=W0120 @@ -272,7 +301,7 @@ def make_rst_docstring(self, width, indent_level, tabsize): else: # end a section with two newlines (note that the section already ends with a newline # if it ends with a paragraph) - output += '\n' * isinstance(paragraph, DocDescription) + output += "\n" * isinstance(paragraph, DocDescription) return output @@ -313,7 +342,7 @@ def __init__(self, contents): If the given content is not a string. """ - self.header = '' + self.header = "" if not isinstance(contents, str): raise TypeError("The parameter `contents` must be a string.") self.contents = [contents] @@ -349,32 +378,47 @@ def make_docstring(self, width, indent_level, tabsize, summary_only=False, speci If the title is too long for the given width and indentation. """ - output = '' + output = "" summary = self.contents[0] # if summary cannot fit into first line with one triple quotation # if len(summary) + ' ' * indent_level * tabsize > width - 3: if len(wrap(summary, width - 3 - int(special), indent_level, tabsize)) > 1: - output += '\n' + output += "\n" # if summary cannot fit into the second line (without tripple quotation) # if len(summary) + ' ' * indent_level * tabsize > width: if len(wrap(summary, width, indent_level, tabsize)) > 1: - raise ValueError('First section of the docstring (summary) must fit completely into' - ' the first line of the docstring (including the triple quotation)' - ' or the second line.') + raise ValueError( + "First section of the docstring (summary) must fit completely into" + " the first line of the docstring (including the triple quotation)" + " or the second line." + ) output += summary # if summary only and summary can fit into the first line with two triple quotations - if not (summary_only and - len(wrap(summary, width - 6 - int(special), indent_level, tabsize)) == 1): - output += '\n\n' + if not ( + summary_only + and len(wrap(summary, width - 6 - int(special), indent_level, tabsize)) == 1 + ): + output += "\n\n" return output # pylint: disable=C0103 -dict_classname_headers = {'ExtendedSummary': '', 'Parameters': None, 'Attributes': None, - 'Methods': None, 'Returns': None, 'Yields': None, - 'OtherParameters': 'other parameters', 'Raises': None, 'Warns': None, - 'Warnings': None, 'SeeAlso': 'see also', 'Notes': None, - 'References': None, 'Examples': None} +dict_classname_headers = { + "ExtendedSummary": "", + "Parameters": None, + "Attributes": None, + "Methods": None, + "Returns": None, + "Yields": None, + "OtherParameters": "other parameters", + "Raises": None, + "Warns": None, + "Warnings": None, + "SeeAlso": "see also", + "Notes": None, + "References": None, + "Examples": None, +} # factory for init @@ -401,6 +445,7 @@ def make_init(header): See https://stackoverflow.com/questions/3431676/creating-functions-in-a-loop for more details. """ + def __init__(self, contents): """Initialize. @@ -434,4 +479,4 @@ def __init__(self, contents): # globals is the dictionary of the current module for the symbols # types is used to instantiate a class (because all classes are instances of type) - globals()[class_name] = type(class_name, (DocSection,), {'__init__': make_init(section_header)}) + globals()[class_name] = type(class_name, (DocSection,), {"__init__": make_init(section_header)}) diff --git a/docinstance/content/test/test_base.py b/docinstance/content/test/test_base.py index 4498f49..43ab044 100644 --- a/docinstance/content/test/test_base.py +++ b/docinstance/content/test/test_base.py @@ -5,6 +5,7 @@ class ModDocContent(DocContent): """DocContent where the init does not raise an error.""" + def __init__(self): pass @@ -27,11 +28,13 @@ def test_base_eq(): class Empty: """Empty class.""" + pass + test2 = Empty() test2.x = 1 assert not test1 == test2 - test2 = {'x': 1} + test2 = {"x": 1} assert not test1 == test2 @@ -47,11 +50,13 @@ def test_base_ne(): class Empty: """Empty class.""" + pass + test2 = Empty() test2.x = 1 assert test1 != test2 - test2 = {'x': 1} + test2 = {"x": 1} assert test1 != test2 diff --git a/docinstance/content/test/test_description.py b/docinstance/content/test/test_description.py index e0992c8..71ef221 100644 --- a/docinstance/content/test/test_description.py +++ b/docinstance/content/test/test_description.py @@ -10,62 +10,62 @@ def test_init(): with pytest.raises(TypeError): DocDescription(2) with pytest.raises(TypeError): - DocDescription('test', signature=1) + DocDescription("test", signature=1) with pytest.raises(TypeError): - DocDescription('test', signature='', types=1) + DocDescription("test", signature="", types=1) with pytest.raises(TypeError): - DocDescription('test', signature='', types=[1]) + DocDescription("test", signature="", types=[1]) with pytest.raises(TypeError): - DocDescription('test', signature='', types={str}) + DocDescription("test", signature="", types={str}) with pytest.raises(TypeError): - DocDescription('test', signature='', types=[str, 1]) + DocDescription("test", signature="", types=[str, 1]) with pytest.raises(TypeError): - DocDescription('test', signature='', types=str, descs=1) + DocDescription("test", signature="", types=str, descs=1) with pytest.raises(TypeError): - DocDescription('test', signature='', types=str, descs=[1]) + DocDescription("test", signature="", types=str, descs=[1]) with pytest.raises(TypeError): - DocDescription('test', signature='', types=str, descs={'1'}) + DocDescription("test", signature="", types=str, descs={"1"}) with pytest.raises(TypeError): - DocDescription('test', signature='', types=str, descs=['1', 2]) + DocDescription("test", signature="", types=str, descs=["1", 2]) - test = DocDescription('test') - assert test.name == 'test' - assert test.signature == '' + test = DocDescription("test") + assert test.name == "test" + assert test.signature == "" assert test.types == [] assert test.descs == [] - test = DocDescription('test', signature='1') - assert test.signature == '1' - test = DocDescription('test', types=str) + test = DocDescription("test", signature="1") + assert test.signature == "1" + test = DocDescription("test", types=str) assert test.types == [str] - test = DocDescription('test', types=(str,)) + test = DocDescription("test", types=(str,)) assert test.types == [str] - test = DocDescription('test', descs='2') - assert test.descs == ['2'] - test = DocDescription('test', descs=('2',)) - assert test.descs == ['2'] + test = DocDescription("test", descs="2") + assert test.descs == ["2"] + test = DocDescription("test", descs=("2",)) + assert test.descs == ["2"] def test_types_str(): """Test DocDescription.types_str.""" - test = DocDescription('test', types=[str, int, 'list of str']) - assert test.types_str == ['str', 'int', 'list of str'] + test = DocDescription("test", types=[str, int, "list of str"]) + assert test.types_str == ["str", "int", "list of str"] def test_make_numpy_docstring(): """Test DocDescription.make_numpy_docstring.""" # no type - test = DocDescription('var_name', descs=['hello']) - assert test.make_numpy_docstring(9, 0, 4) == 'var_name\n hello\n' + test = DocDescription("var_name", descs=["hello"]) + assert test.make_numpy_docstring(9, 0, 4) == "var_name\n hello\n" # one type - test = DocDescription('var_name', types=str, descs=['hello']) + test = DocDescription("var_name", types=str, descs=["hello"]) with pytest.raises(ValueError): test.make_numpy_docstring(13, 0, 4) with pytest.raises(ValueError): test.make_numpy_docstring(14, 1, 2) - assert test.make_numpy_docstring(14, 0, 4) == 'var_name : str\n hello\n' - assert test.make_numpy_docstring(16, 1, 2) == ' var_name : str\n hello\n' + assert test.make_numpy_docstring(14, 0, 4) == "var_name : str\n hello\n" + assert test.make_numpy_docstring(16, 1, 2) == " var_name : str\n hello\n" # multiple types - test = DocDescription('var_name', types=[str, int, bool], descs=['hello']) + test = DocDescription("var_name", types=[str, int, bool], descs=["hello"]) with pytest.raises(ValueError): test.make_numpy_docstring(15, 0, 4) with pytest.raises(ValueError): @@ -74,128 +74,153 @@ def test_make_numpy_docstring(): # the `utils.wrap` complains with pytest.raises(ValueError): test.make_numpy_docstring(16, 0, 4) - assert test.make_numpy_docstring(27, 0, 4) == 'var_name : {str, int, bool}\n hello\n' - assert (test.make_numpy_docstring(26, 0, 4) == - 'var_name : {str, int,\n bool}\n hello\n') - assert (test.make_numpy_docstring(27, 1, 2) == - ' var_name : {str, int,\n bool}\n hello\n') - assert (test.make_numpy_docstring(17, 0, 4) == - 'var_name : {str,\n int,\n bool}\n hello\n') + assert test.make_numpy_docstring(27, 0, 4) == "var_name : {str, int, bool}\n hello\n" + assert ( + test.make_numpy_docstring(26, 0, 4) + == "var_name : {str, int,\n bool}\n hello\n" + ) + assert ( + test.make_numpy_docstring(27, 1, 2) + == " var_name : {str, int,\n bool}\n hello\n" + ) + assert ( + test.make_numpy_docstring(17, 0, 4) + == "var_name : {str,\n int,\n bool}\n hello\n" + ) # signature does nothing - test2 = DocDescription('var_name', signature='(a, b, c)', types=[str, int, bool], - descs=['hello']) + test2 = DocDescription( + "var_name", signature="(a, b, c)", types=[str, int, bool], descs=["hello"] + ) assert test.make_numpy_docstring(17, 0, 4) == test2.make_numpy_docstring(17, 0, 4) # multiple paragraphs - test = DocDescription('var_name', types=[str, int, bool], - descs=['description 1', 'description 2', 'description 3']) - assert (test.make_numpy_docstring(27, 0, 4) == - 'var_name : {str, int, bool}\n description 1\n description 2\n' - ' description 3\n') + test = DocDescription( + "var_name", + types=[str, int, bool], + descs=["description 1", "description 2", "description 3"], + ) + assert ( + test.make_numpy_docstring(27, 0, 4) + == "var_name : {str, int, bool}\n description 1\n description 2\n" + " description 3\n" + ) def test_make_numpy_docstring_signature(): """Test DocDescription.make_numpy_docstring_signature.""" - test = DocDescription('var_name', signature='(a, b, c)', types=[str, int, bool], - descs=['hello']) - assert (test.make_numpy_docstring_signature(36, 0, 4) == - 'var_name(a, b, c) : {str, int, bool}\n hello\n') - assert (test.make_numpy_docstring_signature(26, 0, 4) == - 'var_name(a, b, c) : {str,\n int,\n bool}\n' - ' hello\n') + test = DocDescription( + "var_name", signature="(a, b, c)", types=[str, int, bool], descs=["hello"] + ) + assert ( + test.make_numpy_docstring_signature(36, 0, 4) + == "var_name(a, b, c) : {str, int, bool}\n hello\n" + ) + assert ( + test.make_numpy_docstring_signature(26, 0, 4) + == "var_name(a, b, c) : {str,\n int,\n bool}\n" + " hello\n" + ) def test_make_google_docstring(): """Test DocDescription.make_google_docstring.""" # no type, desc - test = DocDescription('var_name', descs=['hello']) - assert test.make_google_docstring(15, 0, 4) == 'var_name: hello\n' + test = DocDescription("var_name", descs=["hello"]) + assert test.make_google_docstring(15, 0, 4) == "var_name: hello\n" # no type, no descs - test = DocDescription('var_name') - assert test.make_google_docstring(9, 0, 4) == 'var_name:\n' + test = DocDescription("var_name") + assert test.make_google_docstring(9, 0, 4) == "var_name:\n" with pytest.raises(ValueError): test.make_google_docstring(8, 0, 4) # one type, no descs - test = DocDescription('var_name', types=str) - assert test.make_google_docstring(22, 0, 4) == 'var_name (:obj:`str`):\n' + test = DocDescription("var_name", types=str) + assert test.make_google_docstring(22, 0, 4) == "var_name (:obj:`str`):\n" with pytest.raises(ValueError): test.make_google_docstring(21, 0, 4) # one type, no descs - test = DocDescription('var_name', types='str') - assert test.make_google_docstring(15, 0, 4) == 'var_name (str):\n' + test = DocDescription("var_name", types="str") + assert test.make_google_docstring(15, 0, 4) == "var_name (str):\n" with pytest.raises(ValueError): test.make_google_docstring(14, 0, 4) # many types, no descs - test = DocDescription('var_name', types=['str', int]) - assert test.make_google_docstring(22, 0, 4) == ('var_name (str,\n' - ' :obj:`int`):\n') + test = DocDescription("var_name", types=["str", int]) + assert test.make_google_docstring(22, 0, 4) == ("var_name (str,\n" " :obj:`int`):\n") with pytest.raises(ValueError): test.make_google_docstring(21, 0, 4) - assert test.make_google_docstring(23, 1, 1) == (' var_name (str,\n' - ' :obj:`int`):\n') + assert test.make_google_docstring(23, 1, 1) == (" var_name (str,\n" " :obj:`int`):\n") # one type, desc - test = DocDescription('var_name', types=str, descs=['hello']) - assert test.make_google_docstring(22, 0, 4) == 'var_name (:obj:`str`):\n hello\n' + test = DocDescription("var_name", types=str, descs=["hello"]) + assert test.make_google_docstring(22, 0, 4) == "var_name (:obj:`str`):\n hello\n" # FIXME - assert test.make_google_docstring(24, 1, 2) == ' var_name (:obj:`str`):\n hello\n' + assert test.make_google_docstring(24, 1, 2) == " var_name (:obj:`str`):\n hello\n" # multiple types - test = DocDescription('var_name', types=[str, int, bool], descs=['hello']) + test = DocDescription("var_name", types=[str, int, bool], descs=["hello"]) with pytest.raises(ValueError): test.make_google_docstring(22, 0, 4) with pytest.raises(ValueError): test.make_google_docstring(24, 1, 2) - assert test.make_google_docstring(23, 0, 4) == ('var_name (:obj:`str`,\n' - ' :obj:`int`,\n' - ' :obj:`bool`):\n' - ' hello\n') - assert test.make_google_docstring(26, 1, 2) == (' var_name (:obj:`str`,\n' - ' :obj:`int`,\n' - ' :obj:`bool`):\n' - ' hello\n') - assert (test.make_google_docstring(35, 1, 2) == - (' var_name (:obj:`str`, :obj:`int`,\n' - ' :obj:`bool`): hello\n')) + assert test.make_google_docstring(23, 0, 4) == ( + "var_name (:obj:`str`,\n" + " :obj:`int`,\n" + " :obj:`bool`):\n" + " hello\n" + ) + assert test.make_google_docstring(26, 1, 2) == ( + " var_name (:obj:`str`,\n" + " :obj:`int`,\n" + " :obj:`bool`):\n" + " hello\n" + ) + assert test.make_google_docstring(35, 1, 2) == ( + " var_name (:obj:`str`, :obj:`int`,\n" " :obj:`bool`): hello\n" + ) # signature does nothing - test2 = DocDescription('var_name', signature='(a, b, c)', types=[str, int, bool], - descs=['hello']) + test2 = DocDescription( + "var_name", signature="(a, b, c)", types=[str, int, bool], descs=["hello"] + ) assert test.make_google_docstring(23, 0, 4) == test2.make_google_docstring(23, 0, 4) # multiple paragraphs - test = DocDescription('var_name', types=[str, int, bool], - descs=['description 1', 'description 2', 'description 3']) - assert test.make_google_docstring(26, 0, 2) == ('var_name (:obj:`str`,\n' - ' :obj:`int`,\n' - ' :obj:`bool`):\n' - ' description 1\n' - ' description 2\n' - ' description 3\n') + test = DocDescription( + "var_name", + types=[str, int, bool], + descs=["description 1", "description 2", "description 3"], + ) + assert test.make_google_docstring(26, 0, 2) == ( + "var_name (:obj:`str`,\n" + " :obj:`int`,\n" + " :obj:`bool`):\n" + " description 1\n" + " description 2\n" + " description 3\n" + ) def test_make_rst_docstring(): """Test DocDescription.make_rst_docstring.""" # only name - test = DocDescription('var_name') - assert test.make_rst_docstring(16, 0, 1) == ':param var_name:\n' - assert test.make_rst_docstring(17, 1, 1) == ' :param var_name:\n' + test = DocDescription("var_name") + assert test.make_rst_docstring(16, 0, 1) == ":param var_name:\n" + assert test.make_rst_docstring(17, 1, 1) == " :param var_name:\n" with pytest.raises(ValueError): test.make_rst_docstring(15, 0, 4) # name + desc - test = DocDescription('var_name', descs='hello') - assert test.make_rst_docstring(22, 0, 1) == ':param var_name: hello\n' - assert test.make_rst_docstring(21, 0, 1) == (':param var_name:\n' - ' hello\n') - assert test.make_rst_docstring(21, 0, 4) == (':param var_name:\n' - ' hello\n') - test = DocDescription('var_name', descs=['hello my name is', 'Example 2.']) - assert test.make_rst_docstring(25, 0, 4) == (':param var_name: hello my\n' - ' name is\n' - ' Example 2.\n') + test = DocDescription("var_name", descs="hello") + assert test.make_rst_docstring(22, 0, 1) == ":param var_name: hello\n" + assert test.make_rst_docstring(21, 0, 1) == (":param var_name:\n" " hello\n") + assert test.make_rst_docstring(21, 0, 4) == (":param var_name:\n" " hello\n") + test = DocDescription("var_name", descs=["hello my name is", "Example 2."]) + assert test.make_rst_docstring(25, 0, 4) == ( + ":param var_name: hello my\n" " name is\n" " Example 2.\n" + ) # name + type - test = DocDescription('var_name', types=['str', int]) - assert test.make_rst_docstring(20, 0, 2) == (':param var_name:\n' - ':type var_name: str,\n' - ' :obj:`int`\n') + test = DocDescription("var_name", types=["str", int]) + assert test.make_rst_docstring(20, 0, 2) == ( + ":param var_name:\n" ":type var_name: str,\n" " :obj:`int`\n" + ) # name + desc + type - test = DocDescription('var_name', types=['str', int], descs=['Example 1.', 'Example 2.']) - assert test.make_rst_docstring(27, 0, 4) == (':param var_name: Example 1.\n' - ' Example 2.\n' - ':type var_name: str,\n' - ' :obj:`int`\n') + test = DocDescription("var_name", types=["str", int], descs=["Example 1.", "Example 2."]) + assert test.make_rst_docstring(27, 0, 4) == ( + ":param var_name: Example 1.\n" + " Example 2.\n" + ":type var_name: str,\n" + " :obj:`int`\n" + ) diff --git a/docinstance/content/test/test_equation.py b/docinstance/content/test/test_equation.py index 4f8fc0c..f8481f7 100644 --- a/docinstance/content/test/test_equation.py +++ b/docinstance/content/test/test_equation.py @@ -8,36 +8,39 @@ def test_init(): with pytest.raises(TypeError): DocEquation(1) with pytest.raises(TypeError): - DocEquation(['x + 1', 'y + 2']) - test = DocEquation('a + b = 2') - assert test.equations == ['a + b = 2'] - test = DocEquation('a + b &= 2\\\\\nc + d &= 3') - assert test.equations == ['a + b &= 2\\\\', 'c + d &= 3'] - test = DocEquation('a + b &= 2\\\\\nc + d &= 3\\\\\n') - assert test.equations == ['a + b &= 2\\\\', 'c + d &= 3\\\\'] - test = DocEquation('a + b &= 2\\\\\nc + d &= 3\\\\') - assert test.equations == ['a + b &= 2\\\\', 'c + d &= 3\\\\'] + DocEquation(["x + 1", "y + 2"]) + test = DocEquation("a + b = 2") + assert test.equations == ["a + b = 2"] + test = DocEquation("a + b &= 2\\\\\nc + d &= 3") + assert test.equations == ["a + b &= 2\\\\", "c + d &= 3"] + test = DocEquation("a + b &= 2\\\\\nc + d &= 3\\\\\n") + assert test.equations == ["a + b &= 2\\\\", "c + d &= 3\\\\"] + test = DocEquation("a + b &= 2\\\\\nc + d &= 3\\\\") + assert test.equations == ["a + b &= 2\\\\", "c + d &= 3\\\\"] def test_make_numpy_docstring(): """Test DocEquation.make_numpy_docstring.""" - test = DocEquation('a + b = 2') - assert test.make_numpy_docstring(19, 0, 4) == '.. math:: a + b = 2\n\n' - assert test.make_numpy_docstring(18, 0, 4) == '.. math::\n\n a + b = 2\n\n' + test = DocEquation("a + b = 2") + assert test.make_numpy_docstring(19, 0, 4) == ".. math:: a + b = 2\n\n" + assert test.make_numpy_docstring(18, 0, 4) == ".. math::\n\n a + b = 2\n\n" with pytest.raises(ValueError): test.make_numpy_docstring(8, 0, 4) - test = DocEquation('a + b &= 2\\\\\nc + d &= 3\\\\\n') - assert (test.make_numpy_docstring(18, 0, 4) == - '.. math::\n\n' - ' a + b &= 2\\\\\n' - ' c + d &= 3\\\\\n\n') - test = DocEquation('a + b &= 2\\\\\nc + d &= 3\n') - assert (test.make_numpy_docstring(18, 0, 4) == - '.. math::\n\n' - ' a + b &= 2\\\\\n' - ' c + d &= 3\n\n') - test = DocEquation('a + b &= 2\nc + d &= 3\n') - assert (test.make_numpy_docstring(18, 0, 4) == - '.. math::\n\n' - ' a + b &= 2\n' - ' c + d &= 3\n\n') + test = DocEquation("a + b &= 2\\\\\nc + d &= 3\\\\\n") + assert ( + test.make_numpy_docstring(18, 0, 4) == ".. math::\n\n" + " a + b &= 2\\\\\n" + " c + d &= 3\\\\\n\n" + ) + test = DocEquation("a + b &= 2\\\\\nc + d &= 3\n") + assert ( + test.make_numpy_docstring(18, 0, 4) == ".. math::\n\n" + " a + b &= 2\\\\\n" + " c + d &= 3\n\n" + ) + test = DocEquation("a + b &= 2\nc + d &= 3\n") + assert ( + test.make_numpy_docstring(18, 0, 4) == ".. math::\n\n" + " a + b &= 2\n" + " c + d &= 3\n\n" + ) diff --git a/docinstance/content/test/test_section.py b/docinstance/content/test/test_section.py index 833865e..59b7eaa 100644 --- a/docinstance/content/test/test_section.py +++ b/docinstance/content/test/test_section.py @@ -1,313 +1,371 @@ """Test docinstance.content.section.""" import pytest -from docinstance.content.section import (DocSection, Summary, ExtendedSummary, Parameters, - Attributes, Methods, Returns, Yields, OtherParameters, - Raises, Warns, Warnings, SeeAlso, Notes, References, - Examples) +from docinstance.content.section import ( + DocSection, + Summary, + ExtendedSummary, + Parameters, + Attributes, + Methods, + Returns, + Yields, + OtherParameters, + Raises, + Warns, + Warnings, + SeeAlso, + Notes, + References, + Examples, +) from docinstance.content.description import DocDescription def test_init(): """Test DocSection.__init__.""" with pytest.raises(TypeError): - DocSection(1, '') + DocSection(1, "") with pytest.raises(TypeError): - DocSection(['1'], '') + DocSection(["1"], "") with pytest.raises(TypeError): - DocSection('1', 1) + DocSection("1", 1) with pytest.raises(TypeError): - DocSection('1', {'1'}) + DocSection("1", {"1"}) with pytest.raises(TypeError): - DocSection('1', ['1', DocDescription('test')]) + DocSection("1", ["1", DocDescription("test")]) with pytest.raises(TypeError): - DocSection('1', [DocDescription('test'), '1']) - - test = DocSection('header name', 'hello') - assert test.header == 'header name' - assert test.contents == ['hello'] - test = DocSection('header name', ['hello', 'i am']) - assert test.header == 'header name' - assert test.contents == ['hello', 'i am'] - doc1 = DocDescription('hello') - doc2 = DocDescription('i am') - test = DocSection('header name', doc1) - assert test.header == 'header name' + DocSection("1", [DocDescription("test"), "1"]) + + test = DocSection("header name", "hello") + assert test.header == "header name" + assert test.contents == ["hello"] + test = DocSection("header name", ["hello", "i am"]) + assert test.header == "header name" + assert test.contents == ["hello", "i am"] + doc1 = DocDescription("hello") + doc2 = DocDescription("i am") + test = DocSection("header name", doc1) + assert test.header == "header name" assert test.contents == [doc1] - test = DocSection('header name', [doc1, doc2]) - assert test.header == 'header name' + test = DocSection("header name", [doc1, doc2]) + assert test.header == "header name" assert test.contents == [doc1, doc2] def test_make_numpy_docstring(): """Test DocSection.make_numpy_docstring.""" # string content - test = DocSection('header name', 'hello') - assert test.make_numpy_docstring(11, 0, 4) == 'Header Name\n-----------\nhello\n\n' - assert test.make_numpy_docstring(11, 0, 4) == 'Header Name\n-----------\nhello\n\n' + test = DocSection("header name", "hello") + assert test.make_numpy_docstring(11, 0, 4) == "Header Name\n-----------\nhello\n\n" + assert test.make_numpy_docstring(11, 0, 4) == "Header Name\n-----------\nhello\n\n" # multiple string contents - test = DocSection('header name', ['hello', 'i am']) - assert test.make_numpy_docstring(11, 0, 4) == 'Header Name\n-----------\nhello\n\ni am\n\n' + test = DocSection("header name", ["hello", "i am"]) + assert test.make_numpy_docstring(11, 0, 4) == "Header Name\n-----------\nhello\n\ni am\n\n" # doc description - test = DocSection('header name', DocDescription('var_name', types=str, descs='Example.')) - assert (test.make_numpy_docstring(20, 0, 4) == - 'Header Name\n-----------\nvar_name : str\n Example.\n\n') + test = DocSection("header name", DocDescription("var_name", types=str, descs="Example.")) + assert ( + test.make_numpy_docstring(20, 0, 4) + == "Header Name\n-----------\nvar_name : str\n Example.\n\n" + ) # multiple doc descriptions - test = DocSection('header name', [DocDescription('var_name1', types=str, descs='Example 1.'), - DocDescription('var_name2', types=int, descs='Example 2.')]) - assert (test.make_numpy_docstring(20, 0, 4) == - 'Header Name\n-----------\nvar_name1 : str\n Example 1.\nvar_name2 : int\n' - ' Example 2.\n\n') + test = DocSection( + "header name", + [ + DocDescription("var_name1", types=str, descs="Example 1."), + DocDescription("var_name2", types=int, descs="Example 2."), + ], + ) + assert ( + test.make_numpy_docstring(20, 0, 4) + == "Header Name\n-----------\nvar_name1 : str\n Example 1.\nvar_name2 : int\n" + " Example 2.\n\n" + ) # signature does nothing - test = DocSection('header name', - DocDescription('var_name', signature='(a, b)', types=str, descs='Example.')) - assert (test.make_numpy_docstring(20, 0, 4) == - 'Header Name\n-----------\nvar_name : str\n Example.\n\n') + test = DocSection( + "header name", DocDescription("var_name", signature="(a, b)", types=str, descs="Example.") + ) + assert ( + test.make_numpy_docstring(20, 0, 4) + == "Header Name\n-----------\nvar_name : str\n Example.\n\n" + ) def test_make_numpy_docstring_signature(): """Test DocSection.make_numpy_docstring_signature.""" - test = DocSection('header name', - DocDescription('var_name', signature='(a, b)', types=str, descs='Example.')) - assert (test.make_numpy_docstring_signature(20, 0, 4) == - 'Header Name\n-----------\nvar_name(a, b) : str\n Example.\n\n') + test = DocSection( + "header name", DocDescription("var_name", signature="(a, b)", types=str, descs="Example.") + ) + assert ( + test.make_numpy_docstring_signature(20, 0, 4) + == "Header Name\n-----------\nvar_name(a, b) : str\n Example.\n\n" + ) def test_make_google_docstring(): """Test DocSection.make_google_docstring.""" with pytest.raises(ValueError): - test = DocSection('quite long header name', '') + test = DocSection("quite long header name", "") test.make_google_docstring(10, 0, 4) # no header - test = DocSection('', 'Some text.') - assert test.make_google_docstring(10, 0, 4) == ('Some text.\n\n') + test = DocSection("", "Some text.") + assert test.make_google_docstring(10, 0, 4) == ("Some text.\n\n") # one docdescription - test = DocSection('header name', - DocDescription('var_name', signature='(a, b)', types=str, descs='Example.')) - assert test.make_google_docstring(35, 0, 4) == ('Header Name:\n' - ' var_name (:obj:`str`): Example.\n\n') + test = DocSection( + "header name", DocDescription("var_name", signature="(a, b)", types=str, descs="Example.") + ) + assert test.make_google_docstring(35, 0, 4) == ( + "Header Name:\n" " var_name (:obj:`str`): Example.\n\n" + ) # multiple docdescription - test = DocSection('header name', - [DocDescription('var1', signature='(a, b)', types=str, descs='Example1.'), - DocDescription('var2', signature='(c)', types='int', descs='Example2.')]) - assert test.make_google_docstring(35, 0, 4) == ('Header Name:\n' - ' var1 (:obj:`str`): Example1.\n' - ' var2 (int): Example2.\n\n') + test = DocSection( + "header name", + [ + DocDescription("var1", signature="(a, b)", types=str, descs="Example1."), + DocDescription("var2", signature="(c)", types="int", descs="Example2."), + ], + ) + assert test.make_google_docstring(35, 0, 4) == ( + "Header Name:\n" " var1 (:obj:`str`): Example1.\n" " var2 (int): Example2.\n\n" + ) # one string - test = DocSection('header name', 'Some text.') - assert test.make_google_docstring(14, 0, 4) == ('Header Name:\n' - ' Some text.\n\n') - assert test.make_google_docstring(13, 0, 4) == ('Header Name:\n' - ' Some\n' - ' text.\n\n') + test = DocSection("header name", "Some text.") + assert test.make_google_docstring(14, 0, 4) == ("Header Name:\n" " Some text.\n\n") + assert test.make_google_docstring(13, 0, 4) == ("Header Name:\n" " Some\n" " text.\n\n") # multiple string - test = DocSection('header name', ['Some text.', 'Another text.']) - assert test.make_google_docstring(17, 0, 4) == ('Header Name:\n' - ' Some text.\n\n' - ' Another text.\n\n') - assert test.make_google_docstring(14, 0, 4) == ('Header Name:\n' - ' Some text.\n\n' - ' Another\n' - ' text.\n\n') - assert test.make_google_docstring(13, 0, 4) == ('Header Name:\n' - ' Some\n' - ' text.\n\n' - ' Another\n' - ' text.\n\n') + test = DocSection("header name", ["Some text.", "Another text."]) + assert test.make_google_docstring(17, 0, 4) == ( + "Header Name:\n" " Some text.\n\n" " Another text.\n\n" + ) + assert test.make_google_docstring(14, 0, 4) == ( + "Header Name:\n" " Some text.\n\n" " Another\n" " text.\n\n" + ) + assert test.make_google_docstring(13, 0, 4) == ( + "Header Name:\n" " Some\n" " text.\n\n" " Another\n" " text.\n\n" + ) def test_make_rst_docstring(): """Test DocSection.make_rst_docstring.""" # no header - test = DocSection('', 'Some text.') - assert test.make_rst_docstring(10, 0, 4) == ('Some text.\n\n') + test = DocSection("", "Some text.") + assert test.make_rst_docstring(10, 0, 4) == ("Some text.\n\n") # normal header, one docdescription - test = DocSection('header name', - DocDescription('var_name', signature='(a, b)', types=str, descs='Example.')) - assert test.make_rst_docstring(35, 0, 4) == (':Header Name:\n\n' - ':param var_name: Example.\n' - ':type var_name: :obj:`str`\n\n') + test = DocSection( + "header name", DocDescription("var_name", signature="(a, b)", types=str, descs="Example.") + ) + assert test.make_rst_docstring(35, 0, 4) == ( + ":Header Name:\n\n" ":param var_name: Example.\n" ":type var_name: :obj:`str`\n\n" + ) # normal header, multiple docdescription - test = DocSection('header name', - [DocDescription('var1', signature='(a, b)', types=str, descs='Example1.'), - DocDescription('var2', signature='(c)', types='int', descs='Example2.')]) - assert test.make_rst_docstring(35, 0, 4) == (':Header Name:\n\n' - ':param var1: Example1.\n' - ':type var1: :obj:`str`\n' - ':param var2: Example2.\n' - ':type var2: int\n\n') + test = DocSection( + "header name", + [ + DocDescription("var1", signature="(a, b)", types=str, descs="Example1."), + DocDescription("var2", signature="(c)", types="int", descs="Example2."), + ], + ) + assert test.make_rst_docstring(35, 0, 4) == ( + ":Header Name:\n\n" + ":param var1: Example1.\n" + ":type var1: :obj:`str`\n" + ":param var2: Example2.\n" + ":type var2: int\n\n" + ) # normal header, one string - test = DocSection('header name', 'Some text.') - assert test.make_rst_docstring(13, 0, 4) == (':Header Name:\n\n' - 'Some text.\n\n') + test = DocSection("header name", "Some text.") + assert test.make_rst_docstring(13, 0, 4) == (":Header Name:\n\n" "Some text.\n\n") # normal header, multiple string - test = DocSection('header name', ['Some text.', 'Another text.']) - assert test.make_rst_docstring(13, 0, 4) == (':Header Name:\n\n' - 'Some text.\n\n' - 'Another text.\n\n') + test = DocSection("header name", ["Some text.", "Another text."]) + assert test.make_rst_docstring(13, 0, 4) == ( + ":Header Name:\n\n" "Some text.\n\n" "Another text.\n\n" + ) # special header, doc description - test = DocSection('see also', - [DocDescription('var1', signature='(a, b)', types=str, descs='Example1.'), - DocDescription('var2', signature='(c)', types='int', descs='Example2.')]) - assert test.make_rst_docstring(35, 0, 4) == ('.. seealso::\n' - ' :param var1: Example1.\n' - ' :type var1: :obj:`str`\n' - ' :param var2: Example2.\n' - ' :type var2: int\n\n') + test = DocSection( + "see also", + [ + DocDescription("var1", signature="(a, b)", types=str, descs="Example1."), + DocDescription("var2", signature="(c)", types="int", descs="Example2."), + ], + ) + assert test.make_rst_docstring(35, 0, 4) == ( + ".. seealso::\n" + " :param var1: Example1.\n" + " :type var1: :obj:`str`\n" + " :param var2: Example2.\n" + " :type var2: int\n\n" + ) # special header, string - test = DocSection('to do', ['Example 1, something.', 'Example 2.']) - assert test.make_rst_docstring(20, 0, 4) == ('.. todo:: Example 1,\n' - ' something.\n' - ' Example 2.\n\n') + test = DocSection("to do", ["Example 1, something.", "Example 2."]) + assert test.make_rst_docstring(20, 0, 4) == ( + ".. todo:: Example 1,\n" " something.\n" " Example 2.\n\n" + ) def test_section_summary_init(): """Test Summary.__init__.""" with pytest.raises(TypeError): - Summary(['summary']) + Summary(["summary"]) with pytest.raises(TypeError): - Summary(DocDescription('something')) - test = Summary('very very long summary') - assert test.header == '' - assert test.contents == ['very very long summary'] + Summary(DocDescription("something")) + test = Summary("very very long summary") + assert test.header == "" + assert test.contents == ["very very long summary"] def test_section_summary_make_docstring(): """Test Summary.make_docstring.""" - test = Summary('very very long summary') - assert (test.make_docstring(25, 0, 4, summary_only=True, special=False) == - 'very very long summary\n\n') - assert (test.make_docstring(24, 0, 4, summary_only=True, special=False) == - '\nvery very long summary\n\n') - assert (test.make_docstring(28, 0, 4, summary_only=True, special=False) == - 'very very long summary') - assert (test.make_docstring(25, 0, 4, summary_only=False, special=False) == - 'very very long summary\n\n') - assert (test.make_docstring(24, 0, 4, summary_only=False, special=False) == - '\nvery very long summary\n\n') - assert (test.make_docstring(28, 0, 4, summary_only=False, special=False) == - 'very very long summary\n\n') - - assert not (test.make_docstring(25, 0, 4, summary_only=True, special=True) == - 'very very long summary\n\n') - assert (test.make_docstring(26, 0, 4, summary_only=True, special=True) == - 'very very long summary\n\n') - assert not (test.make_docstring(28, 0, 4, summary_only=True, special=True) == - 'very very long summary') - assert (test.make_docstring(29, 0, 4, summary_only=True, special=True) == - 'very very long summary') - - test = Summary('very very very very very very long summary') + test = Summary("very very long summary") + assert ( + test.make_docstring(25, 0, 4, summary_only=True, special=False) + == "very very long summary\n\n" + ) + assert ( + test.make_docstring(24, 0, 4, summary_only=True, special=False) + == "\nvery very long summary\n\n" + ) + assert ( + test.make_docstring(28, 0, 4, summary_only=True, special=False) == "very very long summary" + ) + assert ( + test.make_docstring(25, 0, 4, summary_only=False, special=False) + == "very very long summary\n\n" + ) + assert ( + test.make_docstring(24, 0, 4, summary_only=False, special=False) + == "\nvery very long summary\n\n" + ) + assert ( + test.make_docstring(28, 0, 4, summary_only=False, special=False) + == "very very long summary\n\n" + ) + + assert not ( + test.make_docstring(25, 0, 4, summary_only=True, special=True) + == "very very long summary\n\n" + ) + assert ( + test.make_docstring(26, 0, 4, summary_only=True, special=True) + == "very very long summary\n\n" + ) + assert not ( + test.make_docstring(28, 0, 4, summary_only=True, special=True) == "very very long summary" + ) + assert ( + test.make_docstring(29, 0, 4, summary_only=True, special=True) == "very very long summary" + ) + + test = Summary("very very very very very very long summary") with pytest.raises(ValueError): test.make_docstring(30, 0, 4) def test_section_extended_summary(): """Test ExtendedSummary.__init__.""" - test = ExtendedSummary('This is an extended summary.') - assert test.header == '' - assert test.contents == ['This is an extended summary.'] + test = ExtendedSummary("This is an extended summary.") + assert test.header == "" + assert test.contents == ["This is an extended summary."] def test_section_parameters(): """Test Parameters.__init__.""" - desc1 = DocDescription('a', types=str, descs='Example 1.') - desc2 = DocDescription('b', types=int, descs='Example 2.') + desc1 = DocDescription("a", types=str, descs="Example 1.") + desc2 = DocDescription("b", types=int, descs="Example 2.") test = Parameters([desc1, desc2]) - assert test.header == 'parameters' + assert test.header == "parameters" assert test.contents == [desc1, desc2] def test_section_attributes(): """Test Attributes.__init__.""" - desc1 = DocDescription('a', types=str, descs='Example 1.') - desc2 = DocDescription('b', types=int, descs='Example 2.') + desc1 = DocDescription("a", types=str, descs="Example 1.") + desc2 = DocDescription("b", types=int, descs="Example 2.") test = Attributes([desc1, desc2]) - assert test.header == 'attributes' + assert test.header == "attributes" assert test.contents == [desc1, desc2] def test_section_methods(): """Test Methods.__init__.""" - desc1 = DocDescription('f', signature='(x, y)', types=int, descs='Example 1.') - desc2 = DocDescription('g', signature='(z=1)', types=int, descs='Example 2.') + desc1 = DocDescription("f", signature="(x, y)", types=int, descs="Example 1.") + desc2 = DocDescription("g", signature="(z=1)", types=int, descs="Example 2.") test = Methods([desc1, desc2]) - assert test.header == 'methods' + assert test.header == "methods" assert test.contents == [desc1, desc2] def test_section_returns(): """Test Returns.__init__.""" - desc1 = DocDescription('a', types=int, descs='Example 1.') - desc2 = DocDescription('b', types=str, descs='Example 2.') + desc1 = DocDescription("a", types=int, descs="Example 1.") + desc2 = DocDescription("b", types=str, descs="Example 2.") test = Returns([desc1, desc2]) - assert test.header == 'returns' + assert test.header == "returns" assert test.contents == [desc1, desc2] def test_section_yields(): """Test Yields.__init__.""" - desc = DocDescription('a', types=int, descs='Example 1.') + desc = DocDescription("a", types=int, descs="Example 1.") test = Yields(desc) - assert test.header == 'yields' + assert test.header == "yields" assert test.contents == [desc] def test_section_otherparameters(): """Test OtherParameters.__init__.""" - desc1 = DocDescription('a', types=int, descs='Example 1.') - desc2 = DocDescription('b', types=str, descs='Example 2.') + desc1 = DocDescription("a", types=int, descs="Example 1.") + desc2 = DocDescription("b", types=str, descs="Example 2.") test = OtherParameters([desc1, desc2]) - assert test.header == 'other parameters' + assert test.header == "other parameters" assert test.contents == [desc1, desc2] def test_section_raises(): """Test Raises.__init__.""" - desc = DocDescription('TypeError', descs='If something.') + desc = DocDescription("TypeError", descs="If something.") test = Raises(desc) - assert test.header == 'raises' + assert test.header == "raises" assert test.contents == [desc] def test_section_warns(): """Test Warns.__init__.""" - desc = DocDescription('Warning', descs='If something.') + desc = DocDescription("Warning", descs="If something.") test = Warns(desc) - assert test.header == 'warns' + assert test.header == "warns" assert test.contents == [desc] def test_section_warnings(): """Test Warnings.__init__.""" - test = Warnings('Not to be used.') - assert test.header == 'warnings' - assert test.contents == ['Not to be used.'] + test = Warnings("Not to be used.") + assert test.header == "warnings" + assert test.contents == ["Not to be used."] def test_section_seealso(): """Test SeeAlso.__init__.""" - test = SeeAlso('Some other code.') - assert test.header == 'see also' - assert test.contents == ['Some other code.'] + test = SeeAlso("Some other code.") + assert test.header == "see also" + assert test.contents == ["Some other code."] def test_section_notes(): """Test Notes.__init__.""" - test = Notes('Some comment.') - assert test.header == 'notes' - assert test.contents == ['Some comment.'] + test = Notes("Some comment.") + assert test.header == "notes" + assert test.contents == ["Some comment."] def test_section_references(): """Test References.__init__.""" - test = References('Some reference.') - assert test.header == 'references' - assert test.contents == ['Some reference.'] + test = References("Some reference.") + assert test.header == "references" + assert test.contents == ["Some reference."] def test_section_examples(): """Test Examples.__init__.""" - test = Examples('Some example.') - assert test.header == 'examples' - assert test.contents == ['Some example.'] + test = Examples("Some example.") + assert test.header == "examples" + assert test.contents == ["Some example."] diff --git a/docinstance/docstring.py b/docinstance/docstring.py index 736750e..96c7ba1 100644 --- a/docinstance/docstring.py +++ b/docinstance/docstring.py @@ -22,7 +22,7 @@ class Docstring: """ - def __init__(self, sections, default_style='numpy'): + def __init__(self, sections, default_style="numpy"): """Initialize. Parameters @@ -44,19 +44,26 @@ def __init__(self, sections, default_style='numpy'): """ if isinstance(sections, (str, DocContent)): sections = [sections] - elif not (isinstance(sections, (list, tuple)) and - all(isinstance(i, (str, DocContent)) for i in sections)): - raise TypeError('Sections of the docstring must be provided as a string, list/tuple of ' - 'strings, or list/tuple of DocContent instances.') + elif not ( + isinstance(sections, (list, tuple)) + and all(isinstance(i, (str, DocContent)) for i in sections) + ): + raise TypeError( + "Sections of the docstring must be provided as a string, list/tuple of " + "strings, or list/tuple of DocContent instances." + ) # NOTE: should the empty sections be allowed? elif not sections: - raise ValueError('At least one section must be provided.') - self.sections = [section if isinstance(section, DocSection) else DocSection('', section) - for section in sections] - - if default_style not in ['numpy', 'numpy with signature', 'google', 'rst']: - raise ValueError("Default style must be one of 'numpy', 'numpy with signature', " - "'google', 'rst'.") + raise ValueError("At least one section must be provided.") + self.sections = [ + section if isinstance(section, DocSection) else DocSection("", section) + for section in sections + ] + + if default_style not in ["numpy", "numpy with signature", "google", "rst"]: + raise ValueError( + "Default style must be one of 'numpy', 'numpy with signature', " "'google', 'rst'." + ) self.default_style = default_style # pylint: disable=R0912 @@ -105,56 +112,63 @@ def make_docstring(self, width=100, indent_level=0, tabsize=4, style=None): style = self.default_style # check input if not isinstance(width, int): - raise TypeError('Maximum width of the line must be given as an integer.') + raise TypeError("Maximum width of the line must be given as an integer.") elif width <= 0: - raise ValueError('Maximum width of the line must be greater than zero.') + raise ValueError("Maximum width of the line must be greater than zero.") if not isinstance(indent_level, int): - raise TypeError('Level of indentation must be given as an integer.') + raise TypeError("Level of indentation must be given as an integer.") elif indent_level < 0: - raise ValueError('Level of indentation must be greater than or equal to zero.') + raise ValueError("Level of indentation must be greater than or equal to zero.") if not isinstance(tabsize, int): - raise TypeError('Number of spaces in a tab must be given as an integer.') + raise TypeError("Number of spaces in a tab must be given as an integer.") elif tabsize <= 0: - raise ValueError('Number of spaces in a tab must be greater than zero.') - - if style == 'numpy': - docstring_func = 'make_numpy_docstring' - elif style == 'numpy with signature': - docstring_func = 'make_numpy_docstring_signature' - elif style == 'google': - docstring_func = 'make_google_docstring' - elif style == 'rst': - docstring_func = 'make_rst_docstring' + raise ValueError("Number of spaces in a tab must be greater than zero.") + + if style == "numpy": + docstring_func = "make_numpy_docstring" + elif style == "numpy with signature": + docstring_func = "make_numpy_docstring_signature" + elif style == "google": + docstring_func = "make_google_docstring" + elif style == "rst": + docstring_func = "make_rst_docstring" else: - raise ValueError("Given docstring style must be one of 'numpy', 'numpy with signature'," - " 'google', 'rst'.") + raise ValueError( + "Given docstring style must be one of 'numpy', 'numpy with signature'," + " 'google', 'rst'." + ) # FIXME: this may not be necessary and can be removed if not self.check_section_order(style): - raise ValueError('Sections must be ordered according to the guideline set by the given ' - 'docstring style.') + raise ValueError( + "Sections must be ordered according to the guideline set by the given " + "docstring style." + ) - output = '' + output = "" # check that first section does not have a header - if self.sections[0].header != '': - raise ValueError('First section of the docstring (summary) must have an empty header.') + if self.sections[0].header != "": + raise ValueError("First section of the docstring (summary) must have an empty header.") # add summary summary = Summary(self.sections[0].contents[0]) - output += summary.make_docstring(width, indent_level, tabsize, - summary_only=(len(self.sections) == - len(self.sections[0].contents) == 1)) + output += summary.make_docstring( + width, + indent_level, + tabsize, + summary_only=(len(self.sections) == len(self.sections[0].contents) == 1), + ) # add remaining summary if len(self.sections[0].contents) > 1: - summary = DocSection('', self.sections[0].contents[1:]) + summary = DocSection("", self.sections[0].contents[1:]) output += getattr(summary, docstring_func)(width, indent_level, tabsize) # add other sections if len(self.sections) > 1: for section in self.sections[1:]: output += getattr(section, docstring_func)(width, indent_level, tabsize) # add whitespace to indent the triple quotation - output += ' ' * indent_level * tabsize + output += " " * indent_level * tabsize return output # TODO: add ordering of the other styles. It seems that only numpy cares about the ordering of @@ -186,16 +200,32 @@ def check_section_order(self, style): # sections with values over 99) default_value = 99 # only numpy seems to care about the ordering - if style == 'numpy': - ordering = {'': 0, 'parameters': 1, 'attributes': 2, 'methods': 3, 'returns': 4, - 'yields': 4, 'other parameters': 5, 'raises': 6, 'warns': 7, 'warnings': 8, - 'see also': 9, 'notes': 10, 'references': 11, 'examples': 12} + if style == "numpy": + ordering = { + "": 0, + "parameters": 1, + "attributes": 2, + "methods": 3, + "returns": 4, + "yields": 4, + "other parameters": 5, + "raises": 6, + "warns": 7, + "warnings": 8, + "see also": 9, + "notes": 10, + "references": 11, + "examples": 12, + } allow_other_sections = False else: ordering = {} - order_values = [ordering.get(section.header.lower(), default_value) - for section in self.sections] + order_values = [ + ordering.get(section.header.lower(), default_value) for section in self.sections + ] if not allow_other_sections and any(i == default_value for i in order_values): - raise ValueError('For the docstring style, {0}, the headings of the sections must be ' - 'one of {1}'.format(style, list(ordering.keys()))) + raise ValueError( + "For the docstring style, {0}, the headings of the sections must be " + "one of {1}".format(style, list(ordering.keys())) + ) return all(i <= j for i, j in zip(order_values, order_values[1:])) diff --git a/docinstance/parser/latex.py b/docinstance/parser/latex.py index 1bf741b..3569974 100644 --- a/docinstance/parser/latex.py +++ b/docinstance/parser/latex.py @@ -19,7 +19,7 @@ def is_math(text): False otherwise. """ - re_math = re.compile(r'^\s*\.\.\s*math::\n*(?:\n\s+.+)+\n*$') + re_math = re.compile(r"^\s*\.\.\s*math::\n*(?:\n\s+.+)+\n*$") return bool(re_math.search(text)) @@ -38,17 +38,17 @@ def parse_equation(text): Equations are stored as instances of DocEquation. """ - re_math = re.compile(r'\s*(\.\.\s*math::\n*(?: +.+\n?)+)\n*') + re_math = re.compile(r"\s*(\.\.\s*math::\n*(?: +.+\n?)+)\n*") # split equations split_text = [] for block in re_math.split(text): # remove empty blocks - if block == '': + if block == "": continue # remove trailing newline if is_math(block): - block = re.sub(r'\n*$', '', block) - block = re.sub(r'^\s*\.\.\s*math::\n*', '', block) + block = re.sub(r"\n*$", "", block) + block = re.sub(r"^\s*\.\.\s*math::\n*", "", block) block = inspect.cleandoc(block) block = DocEquation(block) split_text.append(block) diff --git a/docinstance/parser/numpy.py b/docinstance/parser/numpy.py index e726093..786be04 100644 --- a/docinstance/parser/numpy.py +++ b/docinstance/parser/numpy.py @@ -4,10 +4,24 @@ from docinstance.parser.latex import parse_equation from docinstance.docstring import Docstring from docinstance.content.description import DocDescription -from docinstance.content.section import (DocSection, Summary, ExtendedSummary, Parameters, - Attributes, Methods, Returns, Yields, OtherParameters, - Raises, Warns, Warnings, SeeAlso, Notes, References, - Examples) +from docinstance.content.section import ( + DocSection, + Summary, + ExtendedSummary, + Parameters, + Attributes, + Methods, + Returns, + Yields, + OtherParameters, + Raises, + Warns, + Warnings, + SeeAlso, + Notes, + References, + Examples, +) from docinstance.content.equation import DocEquation @@ -47,27 +61,29 @@ def parse_numpy(docstring, contains_quotes=False): Copied from https://github.com/kimt33/pydocstring. """ - docstring = inspect.cleandoc('\n' * contains_quotes + docstring) + docstring = inspect.cleandoc("\n" * contains_quotes + docstring) # remove quotes from docstring if contains_quotes: - quotes = r'[\'\"]{3}' - if re.search(r'^r{0}'.format(quotes), docstring): - raise NotImplementedError('A raw string quotation, i.e. r""" cannot be given as a ' - 'string, i.e. from reading a python file as a string, ' - 'because the backslashes belonging to escape sequences ' - 'cannot be distinguished from those of normal backslash.' - 'You either need to change existing raw string to normal ' - 'i.e. convert all occurences of \\ to \\\\, or import the ' - 'docstring from the instance through `__doc__` attribute.') + quotes = r"[\'\"]{3}" + if re.search(r"^r{0}".format(quotes), docstring): + raise NotImplementedError( + 'A raw string quotation, i.e. r""" cannot be given as a ' + "string, i.e. from reading a python file as a string, " + "because the backslashes belonging to escape sequences " + "cannot be distinguished from those of normal backslash." + "You either need to change existing raw string to normal " + "i.e. convert all occurences of \\ to \\\\, or import the " + "docstring from the instance through `__doc__` attribute." + ) else: - quotes = r'' - docstring = re.sub(r'^{0}'.format(quotes), '', docstring) - docstring = re.sub(r'{0}$'.format(quotes), '', docstring) + quotes = r"" + docstring = re.sub(r"^{0}".format(quotes), "", docstring) + docstring = re.sub(r"{0}$".format(quotes), "", docstring) sections = [] # summary - for regex in [r'^\n?(.+?)\n\n+', r'^\n?(.*?)\n*$']: + for regex in [r"^\n?(.+?)\n\n+", r"^\n?(.*?)\n*$"]: re_summary = re.compile(regex) try: sections.append(Summary(re_summary.search(docstring).group(1))) @@ -75,19 +91,22 @@ def parse_numpy(docstring, contains_quotes=False): except AttributeError: pass else: - raise ValueError('The summary must be in the first or the second line with a blank line ' - 'afterwards.') + raise ValueError( + "The summary must be in the first or the second line with a blank line " "afterwards." + ) # remove summary from docstring - docstring = re_summary.sub('', docstring) - if docstring == '': + docstring = re_summary.sub("", docstring) + if docstring == "": return Docstring(sections) # if headers do not exist - re_header = re.compile(r'\n*(.+)\n(-+)\n+') + re_header = re.compile(r"\n*(.+)\n(-+)\n+") if re_header.search(docstring) is None: # split into blocks by math equations and multiple newlines - extended = [[lines] if isinstance(lines, DocEquation) else re.split(r'\n\n+', lines) - for lines in parse_equation(docstring)] + extended = [ + [lines] if isinstance(lines, DocEquation) else re.split(r"\n\n+", lines) + for lines in parse_equation(docstring) + ] extended = [line for lines in extended for line in lines] extended_contents = [] for block in extended: @@ -97,11 +116,11 @@ def parse_numpy(docstring, contains_quotes=False): # continue if not isinstance(block, DocEquation): # remove quotes - block = re.sub(r'\n*{0}$'.format(quotes), '', block) + block = re.sub(r"\n*{0}$".format(quotes), "", block) # remove trailing newlines - block = re.sub(r'\n+$', '', block) + block = re.sub(r"\n+$", "", block) # replace newlines - block = block.replace('\n', ' ') + block = block.replace("\n", " ") extended_contents.append(block) sections.append(ExtendedSummary(extended_contents)) return Docstring(sections) @@ -113,8 +132,10 @@ def parse_numpy(docstring, contains_quotes=False): extended, *split_docstring = split_docstring # FIXME: repeated code # extract math and split blocks - extended = [[lines] if isinstance(lines, DocEquation) else re.split(r'\n\n+', lines) - for lines in parse_equation(extended)] + extended = [ + [lines] if isinstance(lines, DocEquation) else re.split(r"\n\n+", lines) + for lines in parse_equation(extended) + ] extended = [line for lines in extended for line in lines] # process blocks processed_extended = [] @@ -125,79 +146,95 @@ def parse_numpy(docstring, contains_quotes=False): # continue if not isinstance(block, DocEquation): # remove quotes - block = re.sub(r'\n*{0}$'.format(quotes), '', block) + block = re.sub(r"\n*{0}$".format(quotes), "", block) # remove trailing newlines - block = re.sub(r'\n+$', '', block) + block = re.sub(r"\n+$", "", block) # replace newlines - block = block.replace('\n', ' ') + block = block.replace("\n", " ") processed_extended.append(block) if processed_extended != []: sections.append(ExtendedSummary(processed_extended)) - headers_sections = {'parameters': Parameters, 'other parameters': OtherParameters, - 'attributes': Attributes, 'methods': Methods, 'returns': Returns, - 'yields': Yields, 'raises': Raises, 'see also': SeeAlso, 'warns': Warns, - 'warnings': Warnings, 'examples': Examples, 'references': References, - 'notes': Notes, 'properties': None, 'abstract properties': None, - 'abstract methods': None} - for header, lines, contents in zip(split_docstring[0::3], - split_docstring[1::3], - split_docstring[2::3]): - contents = re.sub(r'\n+$', r'\n', contents) + headers_sections = { + "parameters": Parameters, + "other parameters": OtherParameters, + "attributes": Attributes, + "methods": Methods, + "returns": Returns, + "yields": Yields, + "raises": Raises, + "see also": SeeAlso, + "warns": Warns, + "warnings": Warnings, + "examples": Examples, + "references": References, + "notes": Notes, + "properties": None, + "abstract properties": None, + "abstract methods": None, + } + for header, lines, contents in zip( + split_docstring[0::3], split_docstring[1::3], split_docstring[2::3] + ): + contents = re.sub(r"\n+$", r"\n", contents) if len(header) != len(lines): - raise ValueError('Need {0} of `-` underneath the header title, {1}' - ''.format(len(header), header)) + raise ValueError( + "Need {0} of `-` underneath the header title, {1}" "".format(len(header), header) + ) header = header.lower() header_contents = [] # special headers (special format for each entry) if header in headers_sections: - entries = (entry for entry in re.split(r'\n(?!\s+)', contents) if entry != '') + entries = (entry for entry in re.split(r"\n(?!\s+)", contents) if entry != "") # FIXME: following regular expression would work only if docstring has spaces adjacent # to ':' - re_entry = re.compile(r'^(.+?)(\(.+?\))?(?: *: *(.+))?(?:\n|$)') + re_entry = re.compile(r"^(.+?)(\(.+?\))?(?: *: *(.+))?(?:\n|$)") for entry in entries: # keep only necessary pieces _, name, signature, types, descs = re_entry.split(entry) # process signature if signature is None: - signature = '' + signature = "" else: - signature = ', '.join(i.strip() for i in signature.split(',')) + signature = ", ".join(i.strip() for i in signature.split(",")) # process types if types is None: types = [] - elif re.search(r'\{.+\}', types): - types = re.search(r'^\{((?:(.+?),\s*)*(.+?))\}$', types).group(1) - types = re.split(r',\s*', types) + elif re.search(r"\{.+\}", types): + types = re.search(r"^\{((?:(.+?),\s*)*(.+?))\}$", types).group(1) + types = re.split(r",\s*", types) else: - types = re.search(r'^((?:(.+?),\s*)*(.+?))$', types).group(1) - types = re.split(r',\s*', types) + types = re.search(r"^((?:(.+?),\s*)*(.+?))$", types).group(1) + types = re.split(r",\s*", types) types = [i for i in types if i is not None] # process documentation - descs = inspect.cleandoc('\n' + descs) + descs = inspect.cleandoc("\n" + descs) # NOTE: period is used to terminate a description. i.e. one description is # distinguished from another with a period and a newline. - descs = re.split(r'\.\n+', descs) + descs = re.split(r"\.\n+", descs) # add period (only the last line is not missing the period) - descs = [line + '.' for line in descs[:-1]] + descs[-1:] + descs = [line + "." for line in descs[:-1]] + descs[-1:] # extract equations descs = [line for lines in descs for line in parse_equation(lines)] # non math blocks will replace newlines with spaces. # math blocks will add newline at the end - descs = [line if isinstance(line, DocEquation) else line.replace('\n', ' ') - for line in descs] + descs = [ + line if isinstance(line, DocEquation) else line.replace("\n", " ") + for line in descs + ] # store - header_contents.append(DocDescription(name, signature=signature, types=types, - descs=descs)) + header_contents.append( + DocDescription(name, signature=signature, types=types, descs=descs) + ) else: - header_contents = [i for i in re.split(r'\n\n+', contents) if i != ''] + header_contents = [i for i in re.split(r"\n\n+", contents) if i != ""] try: sections.append(headers_sections[header](header_contents)) except KeyError: diff --git a/docinstance/parser/test/test_latex.py b/docinstance/parser/test/test_latex.py index ffca320..5615bf0 100644 --- a/docinstance/parser/test/test_latex.py +++ b/docinstance/parser/test/test_latex.py @@ -5,37 +5,37 @@ def test_is_math(): """Test docinstance.parser.numpy.is_math.""" - assert is_math('.. math::\n\n x&=2\\\\\n &=3') - assert is_math('.. math::\n\n x&=2\\\\\n &=3\n') - assert is_math('\n.. math::\n\n x&=2\\\\\n &=3\n\n\n') - assert not is_math('x\n.. math::\n\n x&=2\\\\\n &=3\n\n\n') - assert is_math('.. math::\n\n x=2') - assert is_math(' .. math::\n\n x=2') - assert is_math('\n .. math::\n\n x=2') + assert is_math(".. math::\n\n x&=2\\\\\n &=3") + assert is_math(".. math::\n\n x&=2\\\\\n &=3\n") + assert is_math("\n.. math::\n\n x&=2\\\\\n &=3\n\n\n") + assert not is_math("x\n.. math::\n\n x&=2\\\\\n &=3\n\n\n") + assert is_math(".. math::\n\n x=2") + assert is_math(" .. math::\n\n x=2") + assert is_math("\n .. math::\n\n x=2") def test_parse_equation(): """Test docinstance.parser.numpy.parse_equation.""" - test = parse_equation('.. math::\n\n x &= 2\\\\\n &= 3\n') + test = parse_equation(".. math::\n\n x &= 2\\\\\n &= 3\n") assert len(test) == 1 assert isinstance(test[0], DocEquation) - assert test[0].equations == ['x &= 2\\\\', '&= 3'] + assert test[0].equations == ["x &= 2\\\\", "&= 3"] - test = parse_equation('x\n.. math::\n\n x &= 2\\\\\n &= 3\n') + test = parse_equation("x\n.. math::\n\n x &= 2\\\\\n &= 3\n") assert len(test) == 2 - assert test[0] == 'x' + assert test[0] == "x" assert isinstance(test[1], DocEquation) - assert test[1].equations == ['x &= 2\\\\', '&= 3'] + assert test[1].equations == ["x &= 2\\\\", "&= 3"] - test = parse_equation('x\n.. math::\n\n x &= 2\\\\\n &= 3\n\n\n') + test = parse_equation("x\n.. math::\n\n x &= 2\\\\\n &= 3\n\n\n") assert len(test) == 2 - assert test[0] == 'x' + assert test[0] == "x" assert isinstance(test[1], DocEquation) - assert test[1].equations == ['x &= 2\\\\', '&= 3'] + assert test[1].equations == ["x &= 2\\\\", "&= 3"] - test = parse_equation('x\n.. math::\n\n x &= 2\\\\\n &= 3\n\ny') + test = parse_equation("x\n.. math::\n\n x &= 2\\\\\n &= 3\n\ny") assert len(test) == 3 - assert test[0] == 'x' + assert test[0] == "x" assert isinstance(test[1], DocEquation) - assert test[1].equations == ['x &= 2\\\\', '&= 3'] - assert test[2] == 'y' + assert test[1].equations == ["x &= 2\\\\", "&= 3"] + assert test[2] == "y" diff --git a/docinstance/parser/test/test_numpy.py b/docinstance/parser/test/test_numpy.py index 7f35b73..bb44a1a 100644 --- a/docinstance/parser/test/test_numpy.py +++ b/docinstance/parser/test/test_numpy.py @@ -1,7 +1,7 @@ """Tests for docinstance.parser.numpy.""" import pytest from docinstance.docstring import Docstring -from docinstance.content.section import (DocSection, Summary, ExtendedSummary, Parameters) +from docinstance.content.section import DocSection, Summary, ExtendedSummary, Parameters from docinstance.content.description import DocDescription from docinstance.content.equation import DocEquation from docinstance.parser.numpy import parse_numpy @@ -9,33 +9,52 @@ def test_compare_docinstances(): """Test docinstance.parser.test.test_numpy.""" - doc1 = Docstring(['summary', - DocSection('section1', ['content1', 'content2']), - DocSection('section2', [DocDescription('name1', '(a, b)', str, - ['desc1', 'desc2']), - DocDescription('name2', '(c, d)', int, - ['desc3', DocEquation('desc4')])])]) - doc2 = Docstring(['summary', - DocSection('section1', ['content1', 'content2']), - DocSection('section2', [DocDescription('name1', '(a, b)', str, - ['desc1', 'desc2']), - DocDescription('name2', '(c, d)', int, - ['desc3', DocEquation('desc4')])])]) + doc1 = Docstring( + [ + "summary", + DocSection("section1", ["content1", "content2"]), + DocSection( + "section2", + [ + DocDescription("name1", "(a, b)", str, ["desc1", "desc2"]), + DocDescription("name2", "(c, d)", int, ["desc3", DocEquation("desc4")]), + ], + ), + ] + ) + doc2 = Docstring( + [ + "summary", + DocSection("section1", ["content1", "content2"]), + DocSection( + "section2", + [ + DocDescription("name1", "(a, b)", str, ["desc1", "desc2"]), + DocDescription("name2", "(c, d)", int, ["desc3", DocEquation("desc4")]), + ], + ), + ] + ) assert doc1 != 1 - assert Docstring(['a', 'b']) != Docstring(['a', 'b', 'c']) - assert Docstring(['a', 'b']) != Docstring(['a', DocSection('x', 'b')]) - assert Docstring(DocSection('a', 'b')) != Docstring(DocSection('a', ['b', 'c'])) - assert Docstring(DocSection('a', 'b')) != Docstring(DocSection('a', 'c')) - assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) != - Docstring(DocSection('a', DocDescription('1', 'y', 'z', 'k')))) - assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) != - Docstring(DocSection('a', DocDescription('x', '1', 'z', 'k')))) - assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) != - Docstring(DocSection('a', DocDescription('x', 'y', '1', 'k')))) - assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) != - Docstring(DocSection('a', DocDescription('x', 'y', 'z', '1')))) - assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', DocEquation('k')))) != - Docstring(DocSection('a', DocDescription('x', 'y', 'z', DocEquation('1'))))) + assert Docstring(["a", "b"]) != Docstring(["a", "b", "c"]) + assert Docstring(["a", "b"]) != Docstring(["a", DocSection("x", "b")]) + assert Docstring(DocSection("a", "b")) != Docstring(DocSection("a", ["b", "c"])) + assert Docstring(DocSection("a", "b")) != Docstring(DocSection("a", "c")) + assert Docstring(DocSection("a", DocDescription("x", "y", "z", "k"))) != Docstring( + DocSection("a", DocDescription("1", "y", "z", "k")) + ) + assert Docstring(DocSection("a", DocDescription("x", "y", "z", "k"))) != Docstring( + DocSection("a", DocDescription("x", "1", "z", "k")) + ) + assert Docstring(DocSection("a", DocDescription("x", "y", "z", "k"))) != Docstring( + DocSection("a", DocDescription("x", "y", "1", "k")) + ) + assert Docstring(DocSection("a", DocDescription("x", "y", "z", "k"))) != Docstring( + DocSection("a", DocDescription("x", "y", "z", "1")) + ) + assert Docstring(DocSection("a", DocDescription("x", "y", "z", DocEquation("k")))) != Docstring( + DocSection("a", DocDescription("x", "y", "z", DocEquation("1"))) + ) assert doc1.__dict__ == doc2.__dict__ @@ -44,121 +63,184 @@ def test_parse_numpy(): # monkeypatch equality for Docstring Docstring.__eq__ = lambda self, other: self.__dict__ == other.__dict__ # summary - docstring = 'summary' - assert parse_numpy(docstring) == Docstring(Summary('summary')) - docstring = 'summary\n' - assert parse_numpy(docstring) == Docstring(Summary('summary')) - docstring = '\nsummary\n' - assert parse_numpy(docstring) == Docstring(Summary('summary')) + docstring = "summary" + assert parse_numpy(docstring) == Docstring(Summary("summary")) + docstring = "summary\n" + assert parse_numpy(docstring) == Docstring(Summary("summary")) + docstring = "\nsummary\n" + assert parse_numpy(docstring) == Docstring(Summary("summary")) docstring = ' """summary\n """' - assert parse_numpy(docstring, contains_quotes=True) == Docstring(Summary('summary')) + assert parse_numpy(docstring, contains_quotes=True) == Docstring(Summary("summary")) # FIXME: this should raise an error - docstring = '\n\nsummary\n' - assert parse_numpy(docstring), Docstring([Summary('summary')]) + docstring = "\n\nsummary\n" + assert parse_numpy(docstring), Docstring([Summary("summary")]) docstring = '"""\n\nsummary\n"""' with pytest.raises(ValueError): parse_numpy(docstring, contains_quotes=True) docstring = ' """\n\n summary\n """' with pytest.raises(ValueError): parse_numpy(docstring, contains_quotes=True) - docstring = 'summary\na' + docstring = "summary\na" with pytest.raises(ValueError): parse_numpy(docstring) # extended - docstring = 'summary\n\nblock1\n\nblock2' - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), ExtendedSummary(['block1', 'block2'])])) - docstring = '\nsummary\n\nblock1\n\nblock2' - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), ExtendedSummary(['block1', 'block2'])])) - docstring = '\nsummary\n\n\n\nblock2\n\n' - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), ExtendedSummary(['block2'])])) + docstring = "summary\n\nblock1\n\nblock2" + assert parse_numpy(docstring) == Docstring( + [Summary("summary"), ExtendedSummary(["block1", "block2"])] + ) + docstring = "\nsummary\n\nblock1\n\nblock2" + assert parse_numpy(docstring) == Docstring( + [Summary("summary"), ExtendedSummary(["block1", "block2"])] + ) + docstring = "\nsummary\n\n\n\nblock2\n\n" + assert parse_numpy(docstring) == Docstring([Summary("summary"), ExtendedSummary(["block2"])]) # FIXME: is this a bug? - docstring = '\n\nsummary\n\nblock1\n\nblock2' - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), ExtendedSummary(['block1', 'block2'])])) + docstring = "\n\nsummary\n\nblock1\n\nblock2" + assert parse_numpy(docstring) == Docstring( + [Summary("summary"), ExtendedSummary(["block1", "block2"])] + ) # extended + headers - docstring = 'summary\n\nblock1\n\nblock2\n\nheader\n------\nstuff' - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), ExtendedSummary(['block1', 'block2']), - DocSection('header', 'stuff')])) + docstring = "summary\n\nblock1\n\nblock2\n\nheader\n------\nstuff" + assert parse_numpy(docstring) == Docstring( + [Summary("summary"), ExtendedSummary(["block1", "block2"]), DocSection("header", "stuff")] + ) # header with bad divider (-----) - docstring = 'summary\n\nblock1\n\nblock2\n\nheader1\n--\nstuff\n\n' + docstring = "summary\n\nblock1\n\nblock2\n\nheader1\n--\nstuff\n\n" with pytest.raises(ValueError): parse_numpy(docstring) - for header in ['parameters', 'attributes', 'methods', 'returns', 'yields', 'raises', - 'other parameters', 'see also']: + for header in [ + "parameters", + "attributes", + "methods", + "returns", + "yields", + "raises", + "other parameters", + "see also", + ]: # name + multiple descriptions - docstring = ('summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc\n description1.\n' - ' description2.'.format(header.title(), '-'*len(header))) - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - ExtendedSummary(['block1', 'block2']), - DocSection(header, - DocDescription('abc', - descs=['description1.', 'description2.']))])) + docstring = ( + "summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc\n description1.\n" + " description2.".format(header.title(), "-" * len(header)) + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + ExtendedSummary(["block1", "block2"]), + DocSection(header, DocDescription("abc", descs=["description1.", "description2."])), + ] + ) # name + types + multiple descriptions - docstring = ('summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc : str\n description1.\n' - ' description2.'.format(header.title(), '-'*len(header))) - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - ExtendedSummary(['block1', 'block2']), - DocSection(header, - DocDescription('abc', types='str', - descs=['description1.', 'description2.']))])) - docstring = ('summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc : {{str, int}}\n' - ' description1.\n' - ' description2.'.format(header.title(), '-'*len(header))) - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - ExtendedSummary(['block1', 'block2']), - DocSection(header, - DocDescription('abc', types=['str', 'int'], - descs=['description1.', 'description2.']))])) + docstring = ( + "summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc : str\n description1.\n" + " description2.".format(header.title(), "-" * len(header)) + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + ExtendedSummary(["block1", "block2"]), + DocSection( + header, + DocDescription("abc", types="str", descs=["description1.", "description2."]), + ), + ] + ) + docstring = ( + "summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc : {{str, int}}\n" + " description1.\n" + " description2.".format(header.title(), "-" * len(header)) + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + ExtendedSummary(["block1", "block2"]), + DocSection( + header, + DocDescription( + "abc", types=["str", "int"], descs=["description1.", "description2."] + ), + ), + ] + ) # name + signature + multiple descriptions - docstring = ('summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc(x, y)\n description1.\n' - ' description2.'.format(header.title(), '-'*len(header))) - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - ExtendedSummary(['block1', 'block2']), - DocSection(header, - DocDescription('abc', signature='(x, y)', - descs=['description1.', 'description2.']))])) + docstring = ( + "summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc(x, y)\n description1.\n" + " description2.".format(header.title(), "-" * len(header)) + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + ExtendedSummary(["block1", "block2"]), + DocSection( + header, + DocDescription( + "abc", signature="(x, y)", descs=["description1.", "description2."] + ), + ), + ] + ) # name + types + signature + multiple descriptions - docstring = ('summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc(x, y): str\n description1.\n' - ' description2.'.format(header.title(), '-'*len(header))) - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - ExtendedSummary(['block1', 'block2']), - DocSection(header, - DocDescription('abc', types='str', signature='(x, y)', - descs=['description1.', 'description2.']))])) + docstring = ( + "summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc(x, y): str\n description1.\n" + " description2.".format(header.title(), "-" * len(header)) + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + ExtendedSummary(["block1", "block2"]), + DocSection( + header, + DocDescription( + "abc", + types="str", + signature="(x, y)", + descs=["description1.", "description2."], + ), + ), + ] + ) # name + types + signature + multiple descriptions - extended summary - docstring = ('summary\n\n{0}\n{1}\nabc(x, y): str\n description1.\n' - ' description2.'.format(header.title(), '-'*len(header))) - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - DocSection(header, - DocDescription('abc', types='str', signature='(x, y)', - descs=['description1.', 'description2.']))])) + docstring = ( + "summary\n\n{0}\n{1}\nabc(x, y): str\n description1.\n" + " description2.".format(header.title(), "-" * len(header)) + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + DocSection( + header, + DocDescription( + "abc", + types="str", + signature="(x, y)", + descs=["description1.", "description2."], + ), + ), + ] + ) # name + types - docstring = ('summary\n\n{0}\n{1}\nabc: str\ndef: int'.format(header.title(), - '-'*len(header))) - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), DocSection(header, - [DocDescription('abc', types='str'), - DocDescription('def', types='int')])])) + docstring = "summary\n\n{0}\n{1}\nabc: str\ndef: int".format( + header.title(), "-" * len(header) + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + DocSection( + header, [DocDescription("abc", types="str"), DocDescription("def", types="int")] + ), + ] + ) def test_parse_numpy_raw(): """Test pydocstring.numpy_docstring.parse_numpy with raw strings.""" docstring = '"""summary\n\nextended"""' - assert (parse_numpy(docstring, contains_quotes=True) == - Docstring([Summary('summary'), ExtendedSummary('extended')])) + assert parse_numpy(docstring, contains_quotes=True) == Docstring( + [Summary("summary"), ExtendedSummary("extended")] + ) docstring = 'r"""summary\n\nextended"""' with pytest.raises(NotImplementedError): parse_numpy(docstring, contains_quotes=True) @@ -168,54 +250,61 @@ def test_parse_numpy_self(): """Test pydocstring.numpy_docstring.parse_numpy using itself as an example.""" docstring = parse_numpy.__doc__ # summary - assert (parse_numpy(docstring, contains_quotes=False).sections[0].contents[0] == - 'Parse a docstring in numpy format into a Docstring instance.') + assert ( + parse_numpy(docstring, contains_quotes=False).sections[0].contents[0] + == "Parse a docstring in numpy format into a Docstring instance." + ) # extended - assert (parse_numpy(docstring, contains_quotes=False).sections[1].contents == - ['Multiple descriptions of the indented information (e.g. parameters, ' - 'attributes, methods, returns, yields, raises, see also) are ' - 'distinguished from one another with a period. If the period is not ' - 'present, then the description is assumed to be a multiline ' - 'description.']) + assert parse_numpy(docstring, contains_quotes=False).sections[1].contents == [ + "Multiple descriptions of the indented information (e.g. parameters, " + "attributes, methods, returns, yields, raises, see also) are " + "distinguished from one another with a period. If the period is not " + "present, then the description is assumed to be a multiline " + "description." + ] # parameters - assert (parse_numpy(docstring, contains_quotes=False).sections[2].header == - 'parameters') - assert (parse_numpy(docstring, contains_quotes=False).sections[2].contents[0].name == - 'docstring') - assert (parse_numpy(docstring, contains_quotes=False).sections[2].contents[0].types == - ['str']) - assert (parse_numpy(docstring, contains_quotes=False).sections[2].contents[0].descs == - ['Numpy docstring.']) - assert (parse_numpy(docstring, contains_quotes=False).sections[2].contents[1].name == - 'contains_quotes') - assert (parse_numpy(docstring, contains_quotes=False).sections[2].contents[1].types == - ['bool']) - assert (parse_numpy(docstring, contains_quotes=False).sections[2].contents[1].descs == - [r'True if docstring contains \"\"\" or \'\'\'.']) + assert parse_numpy(docstring, contains_quotes=False).sections[2].header == "parameters" + assert parse_numpy(docstring, contains_quotes=False).sections[2].contents[0].name == "docstring" + assert parse_numpy(docstring, contains_quotes=False).sections[2].contents[0].types == ["str"] + assert parse_numpy(docstring, contains_quotes=False).sections[2].contents[0].descs == [ + "Numpy docstring." + ] + assert ( + parse_numpy(docstring, contains_quotes=False).sections[2].contents[1].name + == "contains_quotes" + ) + assert parse_numpy(docstring, contains_quotes=False).sections[2].contents[1].types == ["bool"] + assert parse_numpy(docstring, contains_quotes=False).sections[2].contents[1].descs == [ + r"True if docstring contains \"\"\" or \'\'\'." + ] # returns - assert (parse_numpy(docstring, contains_quotes=False).sections[3].header == - 'returns') - assert (parse_numpy(docstring, contains_quotes=False).sections[3].contents[0].name == - 'docstring') - assert (parse_numpy(docstring, contains_quotes=False).sections[3].contents[0].types == - ['Docstring']) - assert (parse_numpy(docstring, contains_quotes=False).sections[3].contents[0].descs == - ['Instance of Docstring that contains the necessary information.']) + assert parse_numpy(docstring, contains_quotes=False).sections[3].header == "returns" + assert parse_numpy(docstring, contains_quotes=False).sections[3].contents[0].name == "docstring" + assert parse_numpy(docstring, contains_quotes=False).sections[3].contents[0].types == [ + "Docstring" + ] + assert parse_numpy(docstring, contains_quotes=False).sections[3].contents[0].descs == [ + "Instance of Docstring that contains the necessary information." + ] # raises - assert (parse_numpy(docstring, contains_quotes=False).sections[4].header == - 'raises') - assert (parse_numpy(docstring, contains_quotes=False).sections[4].contents[0].name == - 'ValueError') - assert (parse_numpy(docstring, contains_quotes=False).sections[4].contents[0].descs == - ['If summary is not in the first or second line.', - 'If summary is now followed with a blank line.', - 'If number of \'-\' does not match the number of characters in the header.', - 'If given entry of the tabbed information (parameters, attributes, methods, returns, ' - 'yields, raises, see also) had an unexpected pattern.']) - assert (parse_numpy(docstring, contains_quotes=False).sections[4].contents[1].name == - 'NotImplementedError') - assert (parse_numpy(docstring, contains_quotes=False).sections[4].contents[1].descs == - [r'If quotes corresponds to a raw string, i.e. r\"\"\".']) + assert parse_numpy(docstring, contains_quotes=False).sections[4].header == "raises" + assert ( + parse_numpy(docstring, contains_quotes=False).sections[4].contents[0].name == "ValueError" + ) + assert parse_numpy(docstring, contains_quotes=False).sections[4].contents[0].descs == [ + "If summary is not in the first or second line.", + "If summary is now followed with a blank line.", + "If number of '-' does not match the number of characters in the header.", + "If given entry of the tabbed information (parameters, attributes, methods, returns, " + "yields, raises, see also) had an unexpected pattern.", + ] + assert ( + parse_numpy(docstring, contains_quotes=False).sections[4].contents[1].name + == "NotImplementedError" + ) + assert parse_numpy(docstring, contains_quotes=False).sections[4].contents[1].descs == [ + r"If quotes corresponds to a raw string, i.e. r\"\"\"." + ] def test_parse_numpy_equations(): @@ -223,61 +312,95 @@ def test_parse_numpy_equations(): # monkeypatch equality for Docstring Docstring.__eq__ = lambda self, other: self.__dict__ == other.__dict__ # equation in extended - docstring = ('summary\n\n.. math::\n\n \\frac{1}{2}') - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), ExtendedSummary(DocEquation('\\frac{1}{2}'))])) - docstring = ('summary\n\n' - '.. math::\n\n' - ' x &= 2\\\\\n' - ' &= y\\\\\n') - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - ExtendedSummary(DocEquation('x &= 2\\\\\n&= y\\\\'))])) - docstring = ('summary\n\n' - '.. math::\n\n' - ' x &= 2\\\\\n' - ' &= y\\\\\n' - 'Parameters\n' - '----------\n' - 'a') - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - ExtendedSummary(DocEquation('x &= 2\\\\\n&= y\\\\')), - Parameters(DocDescription('a'))])) + docstring = "summary\n\n.. math::\n\n \\frac{1}{2}" + assert parse_numpy(docstring) == Docstring( + [Summary("summary"), ExtendedSummary(DocEquation("\\frac{1}{2}"))] + ) + docstring = "summary\n\n" ".. math::\n\n" " x &= 2\\\\\n" " &= y\\\\\n" + assert parse_numpy(docstring) == Docstring( + [Summary("summary"), ExtendedSummary(DocEquation("x &= 2\\\\\n&= y\\\\"))] + ) + docstring = ( + "summary\n\n" + ".. math::\n\n" + " x &= 2\\\\\n" + " &= y\\\\\n" + "Parameters\n" + "----------\n" + "a" + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + ExtendedSummary(DocEquation("x &= 2\\\\\n&= y\\\\")), + Parameters(DocDescription("a")), + ] + ) # equation in parameter # single line equation - docstring = ('summary\n\nParameters\n----------\na : float\n .. math::\n\n ' - ' \\frac{1}{2}') - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - Parameters(DocDescription('a', types='float', - descs=DocEquation('\\frac{1}{2}')))])) + docstring = ( + "summary\n\nParameters\n----------\na : float\n .. math::\n\n " " \\frac{1}{2}" + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + Parameters(DocDescription("a", types="float", descs=DocEquation("\\frac{1}{2}"))), + ] + ) # multi line equation - docstring = ('summary\n\nParameters\n----------\na : float\n .. math::\n\n' - ' \\frac{1}{2}\\\\\n \\frac{1}{3}') - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - Parameters(DocDescription('a', types='float', - descs=DocEquation('\\frac{1}{2}\\\\\n' - '\\frac{1}{3}\n')))])) + docstring = ( + "summary\n\nParameters\n----------\na : float\n .. math::\n\n" + " \\frac{1}{2}\\\\\n \\frac{1}{3}" + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + Parameters( + DocDescription( + "a", types="float", descs=DocEquation("\\frac{1}{2}\\\\\n" "\\frac{1}{3}\n") + ) + ), + ] + ) # multiple equations - docstring = ('summary\n\nParameters\n----------\na : float\n .. math::\n\n' - ' \\frac{1}{2}\n ..math::\n \\frac{1}{3}') - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - Parameters(DocDescription('a', types='float', - descs=[DocEquation('\\frac{1}{2}'), - DocEquation('\\frac{1}{3}')]))])) + docstring = ( + "summary\n\nParameters\n----------\na : float\n .. math::\n\n" + " \\frac{1}{2}\n ..math::\n \\frac{1}{3}" + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + Parameters( + DocDescription( + "a", + types="float", + descs=[DocEquation("\\frac{1}{2}"), DocEquation("\\frac{1}{3}")], + ) + ), + ] + ) # multiple equations and other descriptions - docstring = ('summary\n\nParameters\n----------\na : float\n Some float.\n .. math::\n\n' - ' \\frac{1}{2}\n\n Yes.\n ..math::\n \\frac{1}{3}\n' - ' This is the float.') - assert (parse_numpy(docstring) == - Docstring([Summary('summary'), - Parameters(DocDescription('a', types='float', - descs=['Some float.', - DocEquation('\\frac{1}{2}'), - 'Yes.', - DocEquation('\\frac{1}{3}'), - 'This is the float.']))])) + docstring = ( + "summary\n\nParameters\n----------\na : float\n Some float.\n .. math::\n\n" + " \\frac{1}{2}\n\n Yes.\n ..math::\n \\frac{1}{3}\n" + " This is the float." + ) + assert parse_numpy(docstring) == Docstring( + [ + Summary("summary"), + Parameters( + DocDescription( + "a", + types="float", + descs=[ + "Some float.", + DocEquation("\\frac{1}{2}"), + "Yes.", + DocEquation("\\frac{1}{3}"), + "This is the float.", + ], + ) + ), + ] + ) diff --git a/docinstance/test/test_docstring.py b/docinstance/test/test_docstring.py index 1d95c1d..839aa52 100644 --- a/docinstance/test/test_docstring.py +++ b/docinstance/test/test_docstring.py @@ -10,51 +10,51 @@ def test_init(): with pytest.raises(TypeError): Docstring(1) with pytest.raises(TypeError): - Docstring({'1'}) + Docstring({"1"}) with pytest.raises(TypeError): Docstring([1]) with pytest.raises(TypeError): - Docstring(['1', 1]) + Docstring(["1", 1]) with pytest.raises(ValueError): Docstring([]) with pytest.raises(ValueError): - Docstring('1', 'nothing') + Docstring("1", "nothing") with pytest.raises(ValueError): - Docstring('1', None) + Docstring("1", None) - test = Docstring('some text') + test = Docstring("some text") assert isinstance(test.sections, list) assert len(test.sections) == 1 assert isinstance(test.sections[0], DocSection) - assert test.sections[0].header == '' - assert test.sections[0].contents == ['some text'] + assert test.sections[0].header == "" + assert test.sections[0].contents == ["some text"] - test = Docstring(DocSection('some header', 'Hello World')) + test = Docstring(DocSection("some header", "Hello World")) assert isinstance(test.sections, list) assert len(test.sections) == 1 assert isinstance(test.sections[0], DocSection) - assert test.sections[0].header == 'some header' - assert test.sections[0].contents == ['Hello World'] + assert test.sections[0].header == "some header" + assert test.sections[0].contents == ["Hello World"] - test = Docstring([DocSection('some header', 'Hello World'), 'some text']) + test = Docstring([DocSection("some header", "Hello World"), "some text"]) assert isinstance(test.sections, list) assert len(test.sections) == 2 assert isinstance(test.sections[0], DocSection) - assert test.sections[0].header == 'some header' - assert test.sections[0].contents == ['Hello World'] + assert test.sections[0].header == "some header" + assert test.sections[0].contents == ["Hello World"] assert isinstance(test.sections[1], DocSection) - assert test.sections[1].header == '' - assert test.sections[1].contents == ['some text'] + assert test.sections[1].header == "" + assert test.sections[1].contents == ["some text"] - test = Docstring('some text', 'numpy') - assert test.default_style == 'numpy' - test = Docstring('some text', 'rst') - assert test.default_style == 'rst' + test = Docstring("some text", "numpy") + assert test.default_style == "numpy" + test = Docstring("some text", "rst") + assert test.default_style == "rst" def test_make_docstring(): """Test Docstring.make_docstring.""" - test = Docstring(['summary', 'extended summary', DocSection('parameters', '')]) + test = Docstring(["summary", "extended summary", DocSection("parameters", "")]) # standard input check with pytest.raises(TypeError): test.make_docstring(width=100.0) @@ -73,81 +73,118 @@ def test_make_docstring(): with pytest.raises(ValueError): test.make_docstring(tabsize=0) with pytest.raises(ValueError): - test.make_docstring(style='random style') + test.make_docstring(style="random style") # bad ordering - test = Docstring(['summary', DocSection('parameters', ''), 'extended summary']) + test = Docstring(["summary", DocSection("parameters", ""), "extended summary"]) with pytest.raises(ValueError): - test.make_docstring(style='numpy') + test.make_docstring(style="numpy") # check summary errors - test = Docstring([DocSection('parameters', DocDescription('something'))]) + test = Docstring([DocSection("parameters", DocDescription("something"))]) with pytest.raises(ValueError): test.make_docstring() # one line summary - test = Docstring('very very long summary') - assert test.make_docstring(width=25) == 'very very long summary\n\n' - assert test.make_docstring(width=24) == '\nvery very long summary\n\n' + test = Docstring("very very long summary") + assert test.make_docstring(width=25) == "very very long summary\n\n" + assert test.make_docstring(width=24) == "\nvery very long summary\n\n" # multiple line summary - test = Docstring(['very very long summary', 'extended summary']) - assert test.make_docstring(width=25) == 'very very long summary\n\nextended summary\n\n' - assert test.make_docstring(width=24) == '\nvery very long summary\n\nextended summary\n\n' - test = Docstring(DocSection('', ['very very long summary', 'extended summary'])) - assert test.make_docstring(width=25) == 'very very long summary\n\nextended summary\n\n' - assert test.make_docstring(width=24) == '\nvery very long summary\n\nextended summary\n\n' + test = Docstring(["very very long summary", "extended summary"]) + assert test.make_docstring(width=25) == "very very long summary\n\nextended summary\n\n" + assert test.make_docstring(width=24) == "\nvery very long summary\n\nextended summary\n\n" + test = Docstring(DocSection("", ["very very long summary", "extended summary"])) + assert test.make_docstring(width=25) == "very very long summary\n\nextended summary\n\n" + assert test.make_docstring(width=24) == "\nvery very long summary\n\nextended summary\n\n" # other sections - test = Docstring([DocSection('', ['very very long summary', 'extended summary']), - DocSection('parameters', - DocDescription('var1', types=str, descs='Example.'))]) - assert (test.make_docstring(width=25) == - 'very very long summary\n\nextended summary\n\n' - 'Parameters\n----------\nvar1 : str\n Example.\n\n') - assert (test.make_docstring(width=24) == - '\nvery very long summary\n\nextended summary\n\n' - 'Parameters\n----------\nvar1 : str\n Example.\n\n') + test = Docstring( + [ + DocSection("", ["very very long summary", "extended summary"]), + DocSection("parameters", DocDescription("var1", types=str, descs="Example.")), + ] + ) + assert ( + test.make_docstring(width=25) == "very very long summary\n\nextended summary\n\n" + "Parameters\n----------\nvar1 : str\n Example.\n\n" + ) + assert ( + test.make_docstring(width=24) == "\nvery very long summary\n\nextended summary\n\n" + "Parameters\n----------\nvar1 : str\n Example.\n\n" + ) # numpy with signature - test = Docstring(['summary', - DocSection('methods', - DocDescription('func1', signature='(a, b)', types=str, - descs='Example.'))]) - assert (test.make_docstring(width=25, style='numpy with signature') == - 'summary\n\nMethods\n-------\nfunc1(a, b) : str\n Example.\n\n') + test = Docstring( + [ + "summary", + DocSection( + "methods", DocDescription("func1", signature="(a, b)", types=str, descs="Example.") + ), + ] + ) + assert ( + test.make_docstring(width=25, style="numpy with signature") + == "summary\n\nMethods\n-------\nfunc1(a, b) : str\n Example.\n\n" + ) # google - assert test.make_docstring(width=25, style='google') == ('summary\n\n' - 'Methods:\n' - ' func1 (:obj:`str`):\n' - ' Example.\n\n') + assert test.make_docstring(width=25, style="google") == ( + "summary\n\n" "Methods:\n" " func1 (:obj:`str`):\n" " Example.\n\n" + ) # rst - assert test.make_docstring(width=25, style='rst') == ('summary\n\n' - ':Methods:\n\n' - ':param func1: Example.\n' - ':type func1: :obj:`str`\n\n') + assert test.make_docstring(width=25, style="rst") == ( + "summary\n\n" ":Methods:\n\n" ":param func1: Example.\n" ":type func1: :obj:`str`\n\n" + ) def test_check_section_order(): """Test Docstring.check_section_order.""" - test = Docstring(['summary', 'extended', DocSection('parameters', ''), DocSection('warns', '')]) - assert test.check_section_order('numpy') is True - test = Docstring(['summary', DocSection('parameters', ''), 'extended', DocSection('warns', '')]) - assert test.check_section_order('numpy') is False - test = Docstring(['summary', 'extended', DocSection('warns', ''), DocSection('parameters', '')]) - assert test.check_section_order('numpy') is False + test = Docstring(["summary", "extended", DocSection("parameters", ""), DocSection("warns", "")]) + assert test.check_section_order("numpy") is True + test = Docstring(["summary", DocSection("parameters", ""), "extended", DocSection("warns", "")]) + assert test.check_section_order("numpy") is False + test = Docstring(["summary", "extended", DocSection("warns", ""), DocSection("parameters", "")]) + assert test.check_section_order("numpy") is False # note that the unidentified seections are not permitted for numpy style - test = Docstring(['summary', DocSection('asdfdsaf', ''), 'extended', DocSection('warns', ''), - DocSection('parameters', '')]) + test = Docstring( + [ + "summary", + DocSection("asdfdsaf", ""), + "extended", + DocSection("warns", ""), + DocSection("parameters", ""), + ] + ) with pytest.raises(ValueError): - test.check_section_order('numpy') - test = Docstring(['summary', 'extended', DocSection('warns', ''), DocSection('parameters', ''), - DocSection('asdfdsaf', '')]) + test.check_section_order("numpy") + test = Docstring( + [ + "summary", + "extended", + DocSection("warns", ""), + DocSection("parameters", ""), + DocSection("asdfdsaf", ""), + ] + ) with pytest.raises(ValueError): - test.check_section_order('numpy') + test.check_section_order("numpy") # other styles do not enforce such ordering - test = Docstring(['summary', DocSection('asdfdsaf', ''), 'extended', DocSection('warns', ''), - DocSection('parameters', '')]) - assert test.check_section_order('rst') is True - assert test.check_section_order('random') is True + test = Docstring( + [ + "summary", + DocSection("asdfdsaf", ""), + "extended", + DocSection("warns", ""), + DocSection("parameters", ""), + ] + ) + assert test.check_section_order("rst") is True + assert test.check_section_order("random") is True # note that the unidentified sections are permitted for other styles # NOTE: following does not actually check for the effect of adding unidentified section for non # numpy style because all sections are unidentified for non numpy styles - test = Docstring(['summary', 'extended', DocSection('warns', ''), DocSection('parameters', ''), - DocSection('asdfdsaf', '')]) - assert test.check_section_order('random') is True + test = Docstring( + [ + "summary", + "extended", + DocSection("warns", ""), + DocSection("parameters", ""), + DocSection("asdfdsaf", ""), + ] + ) + assert test.check_section_order("random") is True diff --git a/docinstance/test/test_utils.py b/docinstance/test/test_utils.py index cc3db89..e8eff2a 100644 --- a/docinstance/test/test_utils.py +++ b/docinstance/test/test_utils.py @@ -6,72 +6,100 @@ def test_wrap(): """Test docinstance.utils.wrap.""" # normal usage - assert (docinstance.utils.wrap('hello my name is', width=5, indent_level=0, tabsize=4) == - ['hello', 'my', 'name', 'is']) - assert (docinstance.utils.wrap('hello my name is', width=6, indent_level=1, tabsize=1) == - [' hello', ' my', ' name', ' is']) - assert (docinstance.utils.wrap('hello my name is', width=10, indent_level=2, tabsize=2) == - [' hello', ' my', ' name', ' is']) - assert (docinstance.utils.wrap('hello\n my', width=20, indent_level=0, tabsize=4) == - ['hello', ' my']) - assert (docinstance.utils.wrap('hello\n my', width=20, indent_level=1, tabsize=4) == - [' hello', ' my']) - assert (docinstance.utils.wrap('.. math:\n\n 1 + 2\n', width=20, indent_level=0, - tabsize=4) == ['.. math:', '', ' 1 + 2', '']) + assert docinstance.utils.wrap("hello my name is", width=5, indent_level=0, tabsize=4) == [ + "hello", + "my", + "name", + "is", + ] + assert docinstance.utils.wrap("hello my name is", width=6, indent_level=1, tabsize=1) == [ + " hello", + " my", + " name", + " is", + ] + assert docinstance.utils.wrap("hello my name is", width=10, indent_level=2, tabsize=2) == [ + " hello", + " my", + " name", + " is", + ] + assert docinstance.utils.wrap("hello\n my", width=20, indent_level=0, tabsize=4) == [ + "hello", + " my", + ] + assert docinstance.utils.wrap("hello\n my", width=20, indent_level=1, tabsize=4) == [ + " hello", + " my", + ] + assert docinstance.utils.wrap( + ".. math:\n\n 1 + 2\n", width=20, indent_level=0, tabsize=4 + ) == [".. math:", "", " 1 + 2", ""] # white spaces - assert (docinstance.utils.wrap(' hello', width=9, indent_level=0, tabsize=4) == - [' hello']) - assert docinstance.utils.wrap(' hello', width=8, indent_level=0, tabsize=4) == ['hello'] - assert docinstance.utils.wrap('hello ', width=8, indent_level=0, tabsize=4) == ['hello'] - assert (docinstance.utils.wrap(' hello my ', width=9, indent_level=0, tabsize=4) == - [' hello', 'my']) - assert (docinstance.utils.wrap(' hello my name is ', width=8, indent_level=0, - tabsize=4) == ['hello', 'my', 'name', 'is']) - assert (docinstance.utils.wrap('\n\n hello my name is\n\n', width=5, indent_level=0, - tabsize=4) == ['', '', 'hello', 'my', 'name', 'is', '', '']) - assert (docinstance.utils.wrap('\n\n hello\n my name is\n\n', width=5, indent_level=0, - tabsize=4) == ['', '', 'hello', ' my', 'name', 'is', '', '']) + assert docinstance.utils.wrap(" hello", width=9, indent_level=0, tabsize=4) == [" hello"] + assert docinstance.utils.wrap(" hello", width=8, indent_level=0, tabsize=4) == ["hello"] + assert docinstance.utils.wrap("hello ", width=8, indent_level=0, tabsize=4) == ["hello"] + assert docinstance.utils.wrap(" hello my ", width=9, indent_level=0, tabsize=4) == [ + " hello", + "my", + ] + assert docinstance.utils.wrap( + " hello my name is ", width=8, indent_level=0, tabsize=4 + ) == ["hello", "my", "name", "is"] + assert docinstance.utils.wrap( + "\n\n hello my name is\n\n", width=5, indent_level=0, tabsize=4 + ) == ["", "", "hello", "my", "name", "is", "", ""] + assert docinstance.utils.wrap( + "\n\n hello\n my name is\n\n", width=5, indent_level=0, tabsize=4 + ) == ["", "", "hello", " my", "name", "is", "", ""] # example - assert (docinstance.utils.wrap(' Text that will be wrapped into different lines such that each' - ' line is indented and is less than the given length.', - width=30) == [' Text that will be wrapped', - 'into different lines such that', - 'each line is indented and is', - 'less than the given length.']) + assert docinstance.utils.wrap( + " Text that will be wrapped into different lines such that each" + " line is indented and is less than the given length.", + width=30, + ) == [ + " Text that will be wrapped", + "into different lines such that", + "each line is indented and is", + "less than the given length.", + ] # too much indentation with pytest.raises(ValueError): - docinstance.utils.wrap('hello my name is', width=5, indent_level=3, tabsize=4) + docinstance.utils.wrap("hello my name is", width=5, indent_level=3, tabsize=4) # long words with pytest.raises(ValueError): - docinstance.utils.wrap('hello my name is', width=1, indent_level=0, tabsize=1) + docinstance.utils.wrap("hello my name is", width=1, indent_level=0, tabsize=1) with pytest.raises(ValueError): - docinstance.utils.wrap('hello my name is', width=5, indent_level=1, tabsize=1) + docinstance.utils.wrap("hello my name is", width=5, indent_level=1, tabsize=1) # subsequent indent - assert (docinstance.utils.wrap('a b c d e', width=4, indent_level=0, tabsize=4, - subsequent_indent='xxx') == - ['a b', 'xxxc', 'xxxd', 'xxxe']) - assert (docinstance.utils.wrap('a b c d e', width=4, indent_level=1, tabsize=1, - subsequent_indent='xx') == - [' a b', ' xxc', ' xxd', ' xxe']) + assert docinstance.utils.wrap( + "a b c d e", width=4, indent_level=0, tabsize=4, subsequent_indent="xxx" + ) == ["a b", "xxxc", "xxxd", "xxxe"] + assert docinstance.utils.wrap( + "a b c d e", width=4, indent_level=1, tabsize=1, subsequent_indent="xx" + ) == [" a b", " xxc", " xxd", " xxe"] with pytest.raises(ValueError): - docinstance.utils.wrap('a b c d e', width=4, indent_level=0, tabsize=4, - subsequent_indent='xxxx') + docinstance.utils.wrap( + "a b c d e", width=4, indent_level=0, tabsize=4, subsequent_indent="xxxx" + ) def test_wrap_indent_subsequent(): """Test docinstance.utils.wrap_indent_subsequent.""" - assert (docinstance.utils.wrap_indent_subsequent('a b c d e', width=4, indent_level=1, - tabsize=3) == - ['a b', ' c', ' d', ' e']) + assert docinstance.utils.wrap_indent_subsequent( + "a b c d e", width=4, indent_level=1, tabsize=3 + ) == ["a b", " c", " d", " e"] with pytest.raises(ValueError): - docinstance.utils.wrap_indent_subsequent('a b c d e', width=4, indent_level=1, tabsize=4) + docinstance.utils.wrap_indent_subsequent("a b c d e", width=4, indent_level=1, tabsize=4) def test_extract_members(): """Test docinstance.utils.extract_members.""" + class Test: # pragma: no cover """Test class.""" + @property def f(self): """Test property.""" @@ -83,4 +111,4 @@ def g(self): h = pytest - assert docinstance.utils.extract_members(Test) == {'f': Test.f, 'g': Test.g} + assert docinstance.utils.extract_members(Test) == {"f": Test.f, "g": Test.g} diff --git a/docinstance/test/test_wrapper.py b/docinstance/test/test_wrapper.py index 7f05fbc..750abd2 100644 --- a/docinstance/test/test_wrapper.py +++ b/docinstance/test/test_wrapper.py @@ -3,8 +3,12 @@ import os import sys import pytest -from docinstance.wrapper import (kwarg_wrapper, docstring, docstring_recursive, - docstring_current_module) +from docinstance.wrapper import ( + kwarg_wrapper, + docstring, + docstring_recursive, + docstring_current_module, +) from docinstance.docstring import Docstring from docinstance.content.section import DocSection from docinstance.content.description import DocDescription @@ -12,6 +16,7 @@ def test_kwarg_wrapper(): """Test docinstance.wrapper.kwarg_wrapper.""" + @kwarg_wrapper def test(func, x=1): """Test function.""" @@ -33,12 +38,14 @@ def f(): # pragma: no cover def f(): # pragma: no cover """Test function.""" pass + assert f.x == 1 @test(x=2) def f(): # pragma: no cover """Test function.""" pass + assert f.x == 2 @@ -58,21 +65,24 @@ def test(): # pragma: no cover pass # NOTE: the indentation can be removed with inspect.cleandoc - assert test.__doc__ == ('Test docstring.\n' - '\n' - ' Parameters\n' - ' ----------\n' - ' x : int\n' - ' Something.\n\n' - ' ') - assert not hasattr(test, '_docinstance') + assert test.__doc__ == ( + "Test docstring.\n" + "\n" + " Parameters\n" + " ----------\n" + " x : int\n" + " Something.\n\n" + " " + ) + assert not hasattr(test, "_docinstance") # _docinstance - docinstance = Docstring([ - 'Test docstring.', - DocSection('parameters', - DocDescription('x', types=int, descs='Something.')) - ]) + docinstance = Docstring( + [ + "Test docstring.", + DocSection("parameters", DocDescription("x", types=int, descs="Something.")), + ] + ) def test(): # pragma: no cover """Test function.""" @@ -85,22 +95,21 @@ def test(): # pragma: no cover test._docinstance = docinstance docstring(test) - assert test.__doc__ == ('Test docstring.\n' - '\n' - 'Parameters\n' - '----------\n' - 'x : int\n' - ' Something.\n\n') + assert test.__doc__ == ( + "Test docstring.\n" "\n" "Parameters\n" "----------\n" "x : int\n" " Something.\n\n" + ) assert test._docinstance == docinstance docstring(test, indent_level=2) - assert test.__doc__ == ('Test docstring.\n' - '\n' - ' Parameters\n' - ' ----------\n' - ' x : int\n' - ' Something.\n\n' - ' ') + assert test.__doc__ == ( + "Test docstring.\n" + "\n" + " Parameters\n" + " ----------\n" + " x : int\n" + " Something.\n\n" + " " + ) assert test._docinstance == docinstance @@ -117,6 +126,7 @@ class Test: # pragma: no cover Something. """ + def f(self): """Test function. @@ -127,62 +137,66 @@ def f(self): """ pass - assert Test.__doc__ == ('Test docstring.\n\n' - ' Attributes\n' - ' ----------\n' - ' x : int\n' - ' Something.\n\n' - ' ') - assert not hasattr(Test, '_docinstance') + assert Test.__doc__ == ( + "Test docstring.\n\n" + " Attributes\n" + " ----------\n" + " x : int\n" + " Something.\n\n" + " " + ) + assert not hasattr(Test, "_docinstance") # docinstance - docinstance1 = Docstring([ - 'Test docstring.', - DocSection('attributes', - DocDescription('x', types=int, descs='Something.')) - ]) - docinstance2 = Docstring([ - 'Test function.', - DocSection('returns', 'nothing') - ]) + docinstance1 = Docstring( + [ + "Test docstring.", + DocSection("attributes", DocDescription("x", types=int, descs="Something.")), + ] + ) + docinstance2 = Docstring(["Test function.", DocSection("returns", "nothing")]) # w/o indentation @docstring class Test: # pragma: no cover """Test class.""" + _docinstance = docinstance1 def f(self): """Some docstring.""" pass + f._docinstance = docinstance2 - assert Test.__doc__ == ('Test docstring.\n\n' - 'Attributes\n' - '----------\n' - 'x : int\n' - ' Something.\n\n') + assert Test.__doc__ == ( + "Test docstring.\n\n" "Attributes\n" "----------\n" "x : int\n" " Something.\n\n" + ) assert Test._docinstance == docinstance1 - assert Test.f.__doc__ == 'Some docstring.' + assert Test.f.__doc__ == "Some docstring." assert Test.f._docinstance == docinstance2 # w/ indentation @docstring(indent_level=1) class Test: # pragma: no cover """Test class.""" + _docinstance = docinstance1 def f(self): """Test function.""" pass + f._docinstance = docinstance2 - assert Test.__doc__ == ('Test docstring.\n\n' - ' Attributes\n' - ' ----------\n' - ' x : int\n' - ' Something.\n\n' - ' ') + assert Test.__doc__ == ( + "Test docstring.\n\n" + " Attributes\n" + " ----------\n" + " x : int\n" + " Something.\n\n" + " " + ) def test_wrapper_docstring_recursive_on_class(): @@ -198,6 +212,7 @@ class Test: # pragma: no cover Something. """ + def f(self): """Test function. @@ -208,125 +223,135 @@ def f(self): """ pass - assert Test.__doc__ == ('Test docstring.\n\n' - ' Attributes\n' - ' ----------\n' - ' x : int\n' - ' Something.\n\n' - ' ') - assert not hasattr(Test, '_docinstance') - assert Test.f.__doc__ == ('Test function.\n\n' - ' Returns\n' - ' -------\n' - ' nothing\n\n' - ' ') - assert not hasattr(Test.f, '_docinstance') + assert Test.__doc__ == ( + "Test docstring.\n\n" + " Attributes\n" + " ----------\n" + " x : int\n" + " Something.\n\n" + " " + ) + assert not hasattr(Test, "_docinstance") + assert Test.f.__doc__ == ( + "Test function.\n\n" + " Returns\n" + " -------\n" + " nothing\n\n" + " " + ) + assert not hasattr(Test.f, "_docinstance") # docinstance - docinstance1 = Docstring([ - 'Test docstring.', - DocSection('attributes', - DocDescription('x', types=int, descs='Something.')) - ]) - docinstance2 = Docstring([ - 'Test function.', - DocSection('returns', 'nothing') - ]) + docinstance1 = Docstring( + [ + "Test docstring.", + DocSection("attributes", DocDescription("x", types=int, descs="Something.")), + ] + ) + docinstance2 = Docstring(["Test function.", DocSection("returns", "nothing")]) # w/o indentation @docstring_recursive class Test: # pragma: no cover """Test class.""" + _docinstance = docinstance1 def f(self): """Test function.""" pass + f._docinstance = docinstance2 - assert Test.__doc__ == ('Test docstring.\n\n' - 'Attributes\n' - '----------\n' - 'x : int\n' - ' Something.\n\n') + assert Test.__doc__ == ( + "Test docstring.\n\n" "Attributes\n" "----------\n" "x : int\n" " Something.\n\n" + ) assert Test._docinstance == docinstance1 - assert Test.f.__doc__ == ('Test function.\n\n' - ' Returns\n' - ' -------\n' - ' nothing\n\n' - ' ') + assert Test.f.__doc__ == ( + "Test function.\n\n" " Returns\n" " -------\n" " nothing\n\n" " " + ) assert Test.f._docinstance == docinstance2 # w indentation @docstring_recursive(indent_level=2) class Test: # pragma: no cover """Test class.""" + _docinstance = docinstance1 def f(self): """Test function.""" pass + f._docinstance = docinstance2 - assert Test.__doc__ == ('Test docstring.\n\n' - ' Attributes\n' - ' ----------\n' - ' x : int\n' - ' Something.\n\n' - ' ') + assert Test.__doc__ == ( + "Test docstring.\n\n" + " Attributes\n" + " ----------\n" + " x : int\n" + " Something.\n\n" + " " + ) assert Test._docinstance == docinstance1 - assert Test.f.__doc__ == ('Test function.\n\n' - ' Returns\n' - ' -------\n' - ' nothing\n\n' - ' ') + assert Test.f.__doc__ == ( + "Test function.\n\n" + " Returns\n" + " -------\n" + " nothing\n\n" + " " + ) assert Test.f._docinstance == docinstance2 def test_docstring_current_module(): """Test docinstance.wrapper.docstring_current_module on the current module.""" global test_docstring_current_module - test_docstring_current_module._docinstance = Docstring([ - 'Test docinstance.wrapper.docstring_current_module on the current module.', - 'Did it work?' - ]) + test_docstring_current_module._docinstance = Docstring( + ["Test docinstance.wrapper.docstring_current_module on the current module.", "Did it work?"] + ) def supplementary_docstring_current_module(): """To be used to test docstring_current_module in test_docstring_current_module.""" pass - supplementary_docstring_current_module._docinstance = Docstring([ - 'Some docstring.', - DocSection('parameters', DocDescription('x', types=int, descs='Example.')) - ]) + supplementary_docstring_current_module._docinstance = Docstring( + [ + "Some docstring.", + DocSection("parameters", DocDescription("x", types=int, descs="Example.")), + ] + ) test_docstring_current_module.f = supplementary_docstring_current_module docstring_current_module() - assert (test_docstring_current_module.__doc__ == - 'Test docinstance.wrapper.docstring_current_module on the current module.\n\n' - ' Did it work?\n\n' - ' ') - - assert (test_docstring_current_module.f.__doc__ == - 'Some docstring.\n\n' - ' Parameters\n' - ' ----------\n' - ' x : int\n' - ' Example.\n\n' - ' ') + assert ( + test_docstring_current_module.__doc__ + == "Test docinstance.wrapper.docstring_current_module on the current module.\n\n" + " Did it work?\n\n" + " " + ) + + assert ( + test_docstring_current_module.f.__doc__ == "Some docstring.\n\n" + " Parameters\n" + " ----------\n" + " x : int\n" + " Example.\n\n" + " " + ) def test_docstring_modify_import(tmp_path): """Test docinstance.wrapper.docstring_modify_import on module `dummy`.""" - assert (not hasattr(importlib._bootstrap_external.SourceFileLoader.exec_module, - 'special_cases')) + assert not hasattr(importlib._bootstrap_external.SourceFileLoader.exec_module, "special_cases") # create temporary directory tmp_dir = tmp_path / "test" tmp_dir.mkdir() # create python file that calls docstring_modify_import path1 = tmp_dir / "dummy1.py" - path1.write_text("from docinstance.wrapper import docstring_modify_import\n" - "docstring_modify_import()") + path1.write_text( + "from docinstance.wrapper import docstring_modify_import\n" "docstring_modify_import()" + ) # create another python file with docstrings path2 = tmp_dir / "dummy2.py" path2.write_text( @@ -347,35 +372,42 @@ class DummyClass: 'Class for testing.', DocSection('attributes', DocDescription('x', types=str, descs='Example.')) ]) - ''') + ''' + ) # create yet another python file with docstring_modify_import path3 = tmp_dir / "dummy3.py" - path3.write_text("from docinstance.wrapper import docstring_modify_import\n" - "docstring_modify_import()") + path3.write_text( + "from docinstance.wrapper import docstring_modify_import\n" "docstring_modify_import()" + ) # check that dummy2 is decorated sys.path.append(str(tmp_dir)) import dummy1 import dummy2 as test - assert (test.__doc__ == - 'Dummy module for testing docinstance.wrapper.docstring_modify_import.') - assert (test.DummyClass.__doc__ == - 'Class for testing.\n\n' - ' Attributes\n' - ' ----------\n' - ' x : str\n' - ' Example.\n\n' - ' ') - assert (importlib._bootstrap_external.SourceFileLoader.exec_module.special_cases == - set([os.path.dirname(dummy1.__file__)])) + + assert test.__doc__ == "Dummy module for testing docinstance.wrapper.docstring_modify_import." + assert ( + test.DummyClass.__doc__ == "Class for testing.\n\n" + " Attributes\n" + " ----------\n" + " x : str\n" + " Example.\n\n" + " " + ) + assert importlib._bootstrap_external.SourceFileLoader.exec_module.special_cases == set( + [os.path.dirname(dummy1.__file__)] + ) # check that multiple function calls does not change anything import dummy3 - assert (importlib._bootstrap_external.SourceFileLoader.exec_module.special_cases == - set([os.path.dirname(dummy3.__file__)])) + + assert importlib._bootstrap_external.SourceFileLoader.exec_module.special_cases == set( + [os.path.dirname(dummy3.__file__)] + ) # import package outside current directory import docinstance.utils + # reload module because it has already been loaded via docinstance.wrapper importlib.reload(docinstance.utils) # check that the objects within docinstance.utils has not been touched by wrapper. diff --git a/docinstance/utils.py b/docinstance/utils.py index 8bee280..e355d65 100644 --- a/docinstance/utils.py +++ b/docinstance/utils.py @@ -48,30 +48,34 @@ def wrap(text, width=100, indent_level=0, tabsize=4, **kwargs): If is a word plus its indentation is longer than the width. """ - kwargs.setdefault('expand_tabs', True) - kwargs.setdefault('replace_whitespace', False) - kwargs.setdefault('drop_whitespace', True) - kwargs.setdefault('break_long_words', False) - kwargs['tabsize'] = tabsize + kwargs.setdefault("expand_tabs", True) + kwargs.setdefault("replace_whitespace", False) + kwargs.setdefault("drop_whitespace", True) + kwargs.setdefault("break_long_words", False) + kwargs["tabsize"] = tabsize if width <= tabsize * indent_level: - raise ValueError('Amount of indentation must be less than the maximum width.') - kwargs['width'] = width - tabsize * indent_level + raise ValueError("Amount of indentation must be less than the maximum width.") + kwargs["width"] = width - tabsize * indent_level # NOTE: uncomment to remove whitespace at the beginning and the end # text = text.strip() # Acknowledge all of the newlines (start, middle, and end) - lines = text.split('\n') + lines = text.split("\n") # wrap each line (separated by newline) separately - wrapped_lines = [wrapped_line - for unwrapped_line in lines - for wrapped_line in - (textwrap.wrap(unwrapped_line, **kwargs) if unwrapped_line != '' else [''])] + wrapped_lines = [ + wrapped_line + for unwrapped_line in lines + for wrapped_line in ( + textwrap.wrap(unwrapped_line, **kwargs) if unwrapped_line != "" else [""] + ) + ] # indent - output = [' ' * tabsize * indent_level + line for line in wrapped_lines] + output = [" " * tabsize * indent_level + line for line in wrapped_lines] if any(len(line) > width for line in output): - raise ValueError('There cannot be any word (after indentation) that exceeds the maximum ' - 'width') + raise ValueError( + "There cannot be any word (after indentation) that exceeds the maximum " "width" + ) return output @@ -102,8 +106,9 @@ def wrap_indent_subsequent(text, width=100, indent_level=0, tabsize=4): discarded only if the first word cannot fit into the given line width with the whitespace. """ - output = wrap(text, width=width, indent_level=0, tabsize=0, - subsequent_indent=' ' * tabsize * indent_level) + output = wrap( + text, width=width, indent_level=0, tabsize=0, subsequent_indent=" " * tabsize * indent_level + ) return output diff --git a/docinstance/wrapper.py b/docinstance/wrapper.py index e5d8e53..5a2689b 100644 --- a/docinstance/wrapper.py +++ b/docinstance/wrapper.py @@ -14,6 +14,7 @@ def kwarg_wrapper(wrapper): (with another wrapper). """ + @wraps(wrapper) def new_wrapper(obj=None, **kwargs): """Reconstruction of the provided wrapper so that keyword arguments are rewritten. @@ -67,7 +68,7 @@ def docstring(obj, width=100, indent_level=0, tabsize=4): """ # TODO: if there is a parser, parse the docstring into docinstance - if not hasattr(obj, '_docinstance'): + if not hasattr(obj, "_docinstance"): return obj # generate new docstring from docinstance # pylint: disable=W0212 @@ -127,7 +128,7 @@ def docstring_recursive(obj, width=100, indent_level=0, tabsize=4): # wrap members for member in extract_members(obj).values(): # recurse for all members of member - docstring_recursive(member, width=width, indent_level=indent_level+1, tabsize=tabsize) + docstring_recursive(member, width=width, indent_level=indent_level + 1, tabsize=tabsize) return obj @@ -153,8 +154,11 @@ def docstring_current_module(width=100, tabsize=4): """ # get the module that called this function - module = next(inspect.getmodule(i[0]) for i in inspect.stack()[1:] # pragma: no branch - if inspect.getmodule(i[0]) is not None) + module = next( + inspect.getmodule(i[0]) + for i in inspect.stack()[1:] # pragma: no branch + if inspect.getmodule(i[0]) is not None + ) # recursively convert # NOTE: docstring for the module will always not be indented docstring_recursive(module, width=width, indent_level=0, tabsize=tabsize) @@ -190,8 +194,11 @@ def docstring_modify_import(width=100, tabsize=4): """ # find the location from which this function is called - parentfile = next(inspect.getsourcefile(i[0]) for i in inspect.stack()[1:] # pragma: no branch - if inspect.getmodule(i[0]) is not None) + parentfile = next( + inspect.getsourcefile(i[0]) + for i in inspect.stack()[1:] # pragma: no branch + if inspect.getmodule(i[0]) is not None + ) parentdir = os.path.dirname(parentfile) def pandora_box(old_import): @@ -211,7 +218,7 @@ def pandora_box(old_import): recursively scanning the __dict__. """ - if not hasattr(old_import, 'special_cases'): + if not hasattr(old_import, "special_cases"): old_import.special_cases = set() old_import.special_cases.add(parentdir) @@ -225,8 +232,10 @@ def new_import(*args, **kwargs): # decorator will be applied only to `exc_module`. module = args[1] sourcefile = inspect.getsourcefile(module) - if any(os.path.commonpath([sourcefile, parent]) == parent - for parent in old_import.special_cases): + if any( + os.path.commonpath([sourcefile, parent]) == parent + for parent in old_import.special_cases + ): docstring_recursive(module, width=width, indent_level=0, tabsize=tabsize) new_import.special_cases = old_import.special_cases From 71be25d7b90fb09bb2ae55cecede38955ef76a89 Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sun, 9 Jun 2019 16:08:47 +0200 Subject: [PATCH 2/5] Move tests to directory in project home --- {docinstance/content/test => tests}/test_base.py | 0 {docinstance/content/test => tests}/test_description.py | 0 {docinstance/test => tests}/test_docstring.py | 0 {docinstance/content/test => tests}/test_equation.py | 0 {docinstance/parser/test => tests}/test_latex.py | 0 {docinstance/parser/test => tests}/test_numpy.py | 0 {docinstance/content/test => tests}/test_section.py | 0 {docinstance/test => tests}/test_utils.py | 0 {docinstance/test => tests}/test_wrapper.py | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {docinstance/content/test => tests}/test_base.py (100%) rename {docinstance/content/test => tests}/test_description.py (100%) rename {docinstance/test => tests}/test_docstring.py (100%) rename {docinstance/content/test => tests}/test_equation.py (100%) rename {docinstance/parser/test => tests}/test_latex.py (100%) rename {docinstance/parser/test => tests}/test_numpy.py (100%) rename {docinstance/content/test => tests}/test_section.py (100%) rename {docinstance/test => tests}/test_utils.py (100%) rename {docinstance/test => tests}/test_wrapper.py (100%) diff --git a/docinstance/content/test/test_base.py b/tests/test_base.py similarity index 100% rename from docinstance/content/test/test_base.py rename to tests/test_base.py diff --git a/docinstance/content/test/test_description.py b/tests/test_description.py similarity index 100% rename from docinstance/content/test/test_description.py rename to tests/test_description.py diff --git a/docinstance/test/test_docstring.py b/tests/test_docstring.py similarity index 100% rename from docinstance/test/test_docstring.py rename to tests/test_docstring.py diff --git a/docinstance/content/test/test_equation.py b/tests/test_equation.py similarity index 100% rename from docinstance/content/test/test_equation.py rename to tests/test_equation.py diff --git a/docinstance/parser/test/test_latex.py b/tests/test_latex.py similarity index 100% rename from docinstance/parser/test/test_latex.py rename to tests/test_latex.py diff --git a/docinstance/parser/test/test_numpy.py b/tests/test_numpy.py similarity index 100% rename from docinstance/parser/test/test_numpy.py rename to tests/test_numpy.py diff --git a/docinstance/content/test/test_section.py b/tests/test_section.py similarity index 100% rename from docinstance/content/test/test_section.py rename to tests/test_section.py diff --git a/docinstance/test/test_utils.py b/tests/test_utils.py similarity index 100% rename from docinstance/test/test_utils.py rename to tests/test_utils.py diff --git a/docinstance/test/test_wrapper.py b/tests/test_wrapper.py similarity index 100% rename from docinstance/test/test_wrapper.py rename to tests/test_wrapper.py From 1c336fce50165ad599064ba8e37031ce165f183f Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sun, 9 Jun 2019 16:32:10 +0200 Subject: [PATCH 3/5] Move python package to src directory This should make the protect against testing against the current directory rather than the installed package --- setup.py | 3 ++- {docinstance => src/docinstance}/__init__.py | 0 {docinstance => src/docinstance}/content/__init__.py | 0 {docinstance => src/docinstance}/content/base.py | 0 {docinstance => src/docinstance}/content/description.py | 0 {docinstance => src/docinstance}/content/equation.py | 0 {docinstance => src/docinstance}/content/section.py | 0 {docinstance => src/docinstance}/docstring.py | 0 {docinstance => src/docinstance}/parser/__init__.py | 0 {docinstance => src/docinstance}/parser/latex.py | 0 {docinstance => src/docinstance}/parser/numpy.py | 0 {docinstance => src/docinstance}/utils.py | 0 {docinstance => src/docinstance}/wrapper.py | 0 13 files changed, 2 insertions(+), 1 deletion(-) rename {docinstance => src/docinstance}/__init__.py (100%) rename {docinstance => src/docinstance}/content/__init__.py (100%) rename {docinstance => src/docinstance}/content/base.py (100%) rename {docinstance => src/docinstance}/content/description.py (100%) rename {docinstance => src/docinstance}/content/equation.py (100%) rename {docinstance => src/docinstance}/content/section.py (100%) rename {docinstance => src/docinstance}/docstring.py (100%) rename {docinstance => src/docinstance}/parser/__init__.py (100%) rename {docinstance => src/docinstance}/parser/latex.py (100%) rename {docinstance => src/docinstance}/parser/numpy.py (100%) rename {docinstance => src/docinstance}/utils.py (100%) rename {docinstance => src/docinstance}/wrapper.py (100%) diff --git a/setup.py b/setup.py index 5cc9191..2531bfd 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,8 @@ long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/kimt33/docinstance", - packages=setuptools.find_packages(), + packages=setuptools.find_packages(where="src"), + package_dir={"": "src"}, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", diff --git a/docinstance/__init__.py b/src/docinstance/__init__.py similarity index 100% rename from docinstance/__init__.py rename to src/docinstance/__init__.py diff --git a/docinstance/content/__init__.py b/src/docinstance/content/__init__.py similarity index 100% rename from docinstance/content/__init__.py rename to src/docinstance/content/__init__.py diff --git a/docinstance/content/base.py b/src/docinstance/content/base.py similarity index 100% rename from docinstance/content/base.py rename to src/docinstance/content/base.py diff --git a/docinstance/content/description.py b/src/docinstance/content/description.py similarity index 100% rename from docinstance/content/description.py rename to src/docinstance/content/description.py diff --git a/docinstance/content/equation.py b/src/docinstance/content/equation.py similarity index 100% rename from docinstance/content/equation.py rename to src/docinstance/content/equation.py diff --git a/docinstance/content/section.py b/src/docinstance/content/section.py similarity index 100% rename from docinstance/content/section.py rename to src/docinstance/content/section.py diff --git a/docinstance/docstring.py b/src/docinstance/docstring.py similarity index 100% rename from docinstance/docstring.py rename to src/docinstance/docstring.py diff --git a/docinstance/parser/__init__.py b/src/docinstance/parser/__init__.py similarity index 100% rename from docinstance/parser/__init__.py rename to src/docinstance/parser/__init__.py diff --git a/docinstance/parser/latex.py b/src/docinstance/parser/latex.py similarity index 100% rename from docinstance/parser/latex.py rename to src/docinstance/parser/latex.py diff --git a/docinstance/parser/numpy.py b/src/docinstance/parser/numpy.py similarity index 100% rename from docinstance/parser/numpy.py rename to src/docinstance/parser/numpy.py diff --git a/docinstance/utils.py b/src/docinstance/utils.py similarity index 100% rename from docinstance/utils.py rename to src/docinstance/utils.py diff --git a/docinstance/wrapper.py b/src/docinstance/wrapper.py similarity index 100% rename from docinstance/wrapper.py rename to src/docinstance/wrapper.py From 628e33119502114098c31a353add9307d9f702a8 Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sun, 9 Jun 2019 17:45:45 +0200 Subject: [PATCH 4/5] Update tox 1. Add environment specifically for linters (black, pydocstring, import-order, pep8 naming, bandit, flake8, pylint) 2. Add config for linters 3. Fix import orders 4. Fix missing docstring 5. Fix elif statements with raise statement --- pylintrc | 28 ----- src/docinstance/content/description.py | 2 +- src/docinstance/content/equation.py | 2 +- src/docinstance/content/section.py | 4 +- src/docinstance/docstring.py | 10 +- src/docinstance/parser/latex.py | 3 +- src/docinstance/parser/numpy.py | 34 +++--- src/docinstance/utils.py | 2 +- src/docinstance/wrapper.py | 5 +- tests/test_base.py | 3 +- tests/test_description.py | 2 +- tests/test_docstring.py | 6 +- tests/test_equation.py | 2 +- tests/test_latex.py | 2 +- tests/test_numpy.py | 6 +- tests/test_section.py | 24 ++-- tests/test_utils.py | 2 +- tests/test_wrapper.py | 17 +-- tox.ini | 149 ++++++++++++++++++++++--- 19 files changed, 201 insertions(+), 102 deletions(-) delete mode 100644 pylintrc diff --git a/pylintrc b/pylintrc deleted file mode 100644 index dd80cd9..0000000 --- a/pylintrc +++ /dev/null @@ -1,28 +0,0 @@ -[MESSAGES CONTROL] -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable=W0223,W0231,E0611 - -# abstract-method (W0223): Used when an abstract method (i.e. raise NotImplementedError) is not overridden in concrete class. -# super-init-not-called (W0231): Used when an ancestor class method has an __init__ method which is not called by a derived class. -# no-name-in-module (E0611): Used when a name cannot be found in a module. - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -# notes=FIXME,XXX,TODO -notes=XXX - - -[DESIGN] - -# Maximum number of argunmnts for function/method -max-args = 6 \ No newline at end of file diff --git a/src/docinstance/content/description.py b/src/docinstance/content/description.py index 2744270..a21fa6d 100644 --- a/src/docinstance/content/description.py +++ b/src/docinstance/content/description.py @@ -1,7 +1,7 @@ """Class for representing a description of objects/errors in the docstring.""" -from docinstance.utils import wrap, wrap_indent_subsequent from docinstance.content.base import DocContent from docinstance.content.equation import DocEquation +from docinstance.utils import wrap, wrap_indent_subsequent class DocDescription(DocContent): diff --git a/src/docinstance/content/equation.py b/src/docinstance/content/equation.py index 5a0e3a6..9500b46 100644 --- a/src/docinstance/content/equation.py +++ b/src/docinstance/content/equation.py @@ -1,6 +1,6 @@ """Class for representing math equations.""" -from docinstance.utils import wrap from docinstance.content.base import DocContent +from docinstance.utils import wrap class DocEquation(DocContent): diff --git a/src/docinstance/content/section.py b/src/docinstance/content/section.py index 7a44571..22e582d 100644 --- a/src/docinstance/content/section.py +++ b/src/docinstance/content/section.py @@ -1,7 +1,7 @@ """Class for representing a section in the docstring.""" -from docinstance.utils import wrap, wrap_indent_subsequent from docinstance.content.base import DocContent from docinstance.content.description import DocDescription +from docinstance.utils import wrap, wrap_indent_subsequent class DocSection(DocContent): @@ -446,7 +446,7 @@ def make_init(header): """ - def __init__(self, contents): + def __init__(self, contents): # noqa: N807 """Initialize. Parameters diff --git a/src/docinstance/docstring.py b/src/docinstance/docstring.py index 96c7ba1..bf2f9fc 100644 --- a/src/docinstance/docstring.py +++ b/src/docinstance/docstring.py @@ -44,7 +44,7 @@ def __init__(self, sections, default_style="numpy"): """ if isinstance(sections, (str, DocContent)): sections = [sections] - elif not ( + if not ( isinstance(sections, (list, tuple)) and all(isinstance(i, (str, DocContent)) for i in sections) ): @@ -53,7 +53,7 @@ def __init__(self, sections, default_style="numpy"): "strings, or list/tuple of DocContent instances." ) # NOTE: should the empty sections be allowed? - elif not sections: + if not sections: raise ValueError("At least one section must be provided.") self.sections = [ section if isinstance(section, DocSection) else DocSection("", section) @@ -113,17 +113,17 @@ def make_docstring(self, width=100, indent_level=0, tabsize=4, style=None): # check input if not isinstance(width, int): raise TypeError("Maximum width of the line must be given as an integer.") - elif width <= 0: + if width <= 0: raise ValueError("Maximum width of the line must be greater than zero.") if not isinstance(indent_level, int): raise TypeError("Level of indentation must be given as an integer.") - elif indent_level < 0: + if indent_level < 0: raise ValueError("Level of indentation must be greater than or equal to zero.") if not isinstance(tabsize, int): raise TypeError("Number of spaces in a tab must be given as an integer.") - elif tabsize <= 0: + if tabsize <= 0: raise ValueError("Number of spaces in a tab must be greater than zero.") if style == "numpy": diff --git a/src/docinstance/parser/latex.py b/src/docinstance/parser/latex.py index 3569974..4857c27 100644 --- a/src/docinstance/parser/latex.py +++ b/src/docinstance/parser/latex.py @@ -1,6 +1,7 @@ """Parser for Latex equations.""" -import re import inspect +import re + from docinstance.content.equation import DocEquation diff --git a/src/docinstance/parser/numpy.py b/src/docinstance/parser/numpy.py index 786be04..9b86782 100644 --- a/src/docinstance/parser/numpy.py +++ b/src/docinstance/parser/numpy.py @@ -1,32 +1,32 @@ """Parser for numpy docstring.""" -import re import inspect -from docinstance.parser.latex import parse_equation -from docinstance.docstring import Docstring +import re + from docinstance.content.description import DocDescription -from docinstance.content.section import ( +from docinstance.content.equation import DocEquation +from docinstance.content.section import ( # pylint: disable=E0611 + Attributes, DocSection, - Summary, + Examples, ExtendedSummary, - Parameters, - Attributes, Methods, - Returns, - Yields, + Notes, OtherParameters, + Parameters, Raises, - Warns, - Warnings, - SeeAlso, - Notes, References, - Examples, + Returns, + SeeAlso, + Summary, + Warnings, + Warns, + Yields, ) -from docinstance.content.equation import DocEquation +from docinstance.docstring import Docstring +from docinstance.parser.latex import parse_equation -# pylint: disable=R0912,R0914,R0915 -def parse_numpy(docstring, contains_quotes=False): +def parse_numpy(docstring, contains_quotes=False): # pylint: disable=R0912,R0914,R0915 r"""Parse a docstring in numpy format into a Docstring instance. Multiple descriptions of the indented information (e.g. parameters, attributes, methods, diff --git a/src/docinstance/utils.py b/src/docinstance/utils.py index e355d65..a7b2cde 100644 --- a/src/docinstance/utils.py +++ b/src/docinstance/utils.py @@ -1,7 +1,7 @@ """Utility functions for handling strings and attributes of an object.""" -import textwrap import inspect import os +import textwrap def wrap(text, width=100, indent_level=0, tabsize=4, **kwargs): diff --git a/src/docinstance/wrapper.py b/src/docinstance/wrapper.py index 5a2689b..39d2a5a 100644 --- a/src/docinstance/wrapper.py +++ b/src/docinstance/wrapper.py @@ -1,8 +1,9 @@ """Functions for wrapping a python object to utilize the docinstance object.""" -import inspect -import os from functools import wraps import importlib +import inspect +import os + from docinstance.utils import extract_members diff --git a/tests/test_base.py b/tests/test_base.py index 43ab044..85f7321 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,12 +1,13 @@ """Test docinstance.content.base.""" -import pytest from docinstance.content.base import DocContent +import pytest class ModDocContent(DocContent): """DocContent where the init does not raise an error.""" def __init__(self): + """Initialize.""" pass diff --git a/tests/test_description.py b/tests/test_description.py index 71ef221..f940b1d 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -1,6 +1,6 @@ """Test docinstance.content.description.""" -import pytest from docinstance.content.description import DocDescription +import pytest def test_init(): diff --git a/tests/test_docstring.py b/tests/test_docstring.py index 839aa52..50f532b 100644 --- a/tests/test_docstring.py +++ b/tests/test_docstring.py @@ -1,8 +1,8 @@ """Test docinstance.docstring.""" -import pytest -from docinstance.docstring import Docstring -from docinstance.content.section import DocSection from docinstance.content.description import DocDescription +from docinstance.content.section import DocSection +from docinstance.docstring import Docstring +import pytest def test_init(): diff --git a/tests/test_equation.py b/tests/test_equation.py index f8481f7..3e93253 100644 --- a/tests/test_equation.py +++ b/tests/test_equation.py @@ -1,6 +1,6 @@ """Test docinstance.content.equation.""" -import pytest from docinstance.content.equation import DocEquation +import pytest def test_init(): diff --git a/tests/test_latex.py b/tests/test_latex.py index 5615bf0..5f07788 100644 --- a/tests/test_latex.py +++ b/tests/test_latex.py @@ -1,6 +1,6 @@ """Tests for docinstance.parser.latex.""" -from docinstance.parser.latex import is_math, parse_equation from docinstance.content.equation import DocEquation +from docinstance.parser.latex import is_math, parse_equation def test_is_math(): diff --git a/tests/test_numpy.py b/tests/test_numpy.py index bb44a1a..95f04c6 100644 --- a/tests/test_numpy.py +++ b/tests/test_numpy.py @@ -1,10 +1,10 @@ """Tests for docinstance.parser.numpy.""" -import pytest -from docinstance.docstring import Docstring -from docinstance.content.section import DocSection, Summary, ExtendedSummary, Parameters from docinstance.content.description import DocDescription from docinstance.content.equation import DocEquation +from docinstance.content.section import DocSection, ExtendedSummary, Parameters, Summary +from docinstance.docstring import Docstring from docinstance.parser.numpy import parse_numpy +import pytest def test_compare_docinstances(): diff --git a/tests/test_section.py b/tests/test_section.py index 59b7eaa..6133c4a 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -1,24 +1,24 @@ """Test docinstance.content.section.""" -import pytest +from docinstance.content.description import DocDescription from docinstance.content.section import ( + Attributes, DocSection, - Summary, + Examples, ExtendedSummary, - Parameters, - Attributes, Methods, - Returns, - Yields, + Notes, OtherParameters, + Parameters, Raises, - Warns, - Warnings, - SeeAlso, - Notes, References, - Examples, + Returns, + SeeAlso, + Summary, + Warnings, + Warns, + Yields, ) -from docinstance.content.description import DocDescription +import pytest def test_init(): diff --git a/tests/test_utils.py b/tests/test_utils.py index e8eff2a..e69e0fd 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ """Test docinstance.utils.""" -import pytest import docinstance.utils +import pytest def test_wrap(): diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 750abd2..42d1cd7 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -2,16 +2,17 @@ import importlib import os import sys -import pytest + +from docinstance.content.description import DocDescription +from docinstance.content.section import DocSection +from docinstance.docstring import Docstring from docinstance.wrapper import ( - kwarg_wrapper, docstring, - docstring_recursive, docstring_current_module, + docstring_recursive, + kwarg_wrapper, ) -from docinstance.docstring import Docstring -from docinstance.content.section import DocSection -from docinstance.content.description import DocDescription +import pytest def test_kwarg_wrapper(): @@ -164,7 +165,7 @@ class Test: # pragma: no cover _docinstance = docinstance1 def f(self): - """Some docstring.""" + """Do nothing.""" pass f._docinstance = docinstance2 @@ -173,7 +174,7 @@ def f(self): "Test docstring.\n\n" "Attributes\n" "----------\n" "x : int\n" " Something.\n\n" ) assert Test._docinstance == docinstance1 - assert Test.f.__doc__ == "Some docstring." + assert Test.f.__doc__ == "Do nothing." assert Test.f._docinstance == docinstance2 # w/ indentation diff --git a/tox.ini b/tox.ini index 0a56327..46f645e 100644 --- a/tox.ini +++ b/tox.ini @@ -3,24 +3,147 @@ envlist = py37 py36 py35 - -[flake8] -max-line-length = 100 - -[pycodestyle] -max-line-length = 100 + linters [testenv] deps = pytest pytest-cov +commands = + pytest --cov={envsitepackagesdir}/docinstance --cov-branch tests + +# Linters +[testenv:readme] +basepython = python3 +deps = + readme_renderer + twine +commands = + python setup.py sdist bdist_wheel + twine check dist/* + +[testenv:black] +basepython = python3 +skip_install = true +deps = + black +commands = + black -l 100 --check ./ + black -l 100 --diff ./ + +[testenv:flake8] +basepython = python3 +skip_install = true +deps = flake8 + flake8-docstrings>=0.2.7 + flake8-import-order>=0.9 + pep8-naming + flake8-colors +commands = + flake8 src/docinstance tests/ setup.py + +[testenv:pylint] +basepython = python3 +skip_install = true +deps = pylint - pydocstyle - pycodestyle commands = - flake8 - pylint docinstance - pydocstyle - pycodestyle - pytest --cov={envsitepackagesdir}/docinstance --cov-branch + pylint src/docinstance --rcfile=tox.ini + +[testenv:bandit] +basepython = python3 +skip_install = true +deps = + bandit +commands = + bandit -r src/docinstance + +[testenv:linters] +basepython = python3 +skip_install = true +deps = + {[testenv:black]deps} + {[testenv:flake8]deps} + {[testenv:pylint]deps} + {[testenv:readme]deps} + {[testenv:bandit]deps} +commands = + {[testenv:black]commands} + {[testenv:flake8]commands} + {[testenv:pylint]commands} + {[testenv:readme]commands} + {[testenv:bandit]commands} +ignore_errors = true + +# flake8 +[flake8] +max-line-length = 100 +import-order-style = google +ignore = + # E121 : continuation line under-indented for hanging indent + E121, + # E123 : closing bracket does not match indentation of opening bracket’s line + E123, + # E126 : continuation line over-indented for hanging indent + E126, + # E226 : missing whitespace around arithmetic operator + E226, + # E241 : multiple spaces after ‘,’ + # E242 : tab after ‘,’ + E24, + # E704 : multiple statements on one line (def) + E704, + # W503 : line break occurred before a binary operator + W503, + # W504 : Line break occurred after a binary operator + W504, + # E203 : Whitespace before ':' + E203, + # D202 : No blank lines allowed after function docstring + D202, + +# pylintrc +[FORMAT] +# Maximum number of characters on a single line. +max-line-length=100 + +[MESSAGES CONTROL] +disable= + # attribute-defined-outside-init (W0201): + # Attribute %r defined outside __init__ Used when an instance attribute is + # defined outside the __init__ method. + W0201, + # too-many-instance-attributes (R0902): + # Too many instance attributes (%s/%s) Used when class has too many instance + # attributes, try to reduce this to get a simpler (and so easier to use) + # class. + R0902, + # too-many-arguments (R0913): + # Too many arguments (%s/%s) Used when a function or method takes too many + # arguments. + R0913, + # fixme (W0511): + # Used when a warning note as FIXME or XXX is detected. + W0511, + # bad-continuation (C0330): + # Wrong hanging indentation before block (add 4 spaces). + C0330, + # wrong-import-order (C0411): + # %s comes before %s Used when PEP8 import order is not respected (standard + # imports first, then third-party libraries, then local imports) + C0411, + # arguments-differ (W0221): + # Parameters differ from %s %r method Used when a method has a different + # number of arguments than in the implemented interface or in an overridden + # method. + W0221, + # import-error (E0401): + # Unable to import %s Used when pylint has been unable to import a module. + E0401, + # super-init-not-called (W0231): + # Used when an ancestor class method has an __init__ method which is not called by a derived class. + W0231, + +[SIMILARITIES] +min-similarity-lines=5 \ No newline at end of file From 062e8fc5706b22fb074b2c8d03ea835d3096ee6c Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sun, 9 Jun 2019 17:57:50 +0200 Subject: [PATCH 5/5] Fix coverage issue Branch was not getting covered because generator was not being exhausted. That's not an issue because only the first entry is interesting --- src/docinstance/wrapper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docinstance/wrapper.py b/src/docinstance/wrapper.py index 39d2a5a..6b0e799 100644 --- a/src/docinstance/wrapper.py +++ b/src/docinstance/wrapper.py @@ -155,7 +155,7 @@ def docstring_current_module(width=100, tabsize=4): """ # get the module that called this function - module = next( + module = next( # pragma: no branch inspect.getmodule(i[0]) for i in inspect.stack()[1:] # pragma: no branch if inspect.getmodule(i[0]) is not None @@ -195,7 +195,7 @@ def docstring_modify_import(width=100, tabsize=4): """ # find the location from which this function is called - parentfile = next( + parentfile = next( # pragma: no branch inspect.getsourcefile(i[0]) for i in inspect.stack()[1:] # pragma: no branch if inspect.getmodule(i[0]) is not None