def test_ill_formed_docstring(self): docstring = """Docstring summary. args: raises :: : pathological docstrings should not fail, and ideally should behave reasonably. """ docstrings.parse(docstring)
def test_numpy_colon_in_description(self): docstring = """ Greets name. Arguments --------- name : str name, default : World arg2 : int arg2, default:None arg3 : bool """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='Greets name.', description=None, args=[ ArgInfo(name='name', type='str', description='name, default : World'), ArgInfo(name='arg2', type='int', description='arg2, default:None'), ArgInfo(name='arg3', type='bool', description=None), ]) self.assertEqual(expected_docstring_info, docstring_info)
def HelpString(component, trace=None, verbose=False): """Returns a help string for a supplied component. The component can be any Python class, object, function, module, etc. Args: component: The component to determine the help string for. trace: The Fire trace leading to this component. verbose: Whether to include private members in the help string. Returns: String suitable for display giving information about the component. """ info = inspectutils.Info(component) # TODO(dbieber): Stop using UsageString in favor of UsageText. info['usage'] = UsageString(component, trace, verbose) info['docstring_info'] = docstrings.parse(info['docstring']) is_error_screen = False if trace: is_error_screen = trace.HasError() if is_error_screen: # TODO(dbieber): Call UsageText instead of CommonHelpText once ready. return _CommonHelpText(info, trace) else: return _HelpText(info, trace)
def testHelpScreenWithLineBreak(self): component = tc.ClassWithMultilineDocstring.example_generator t = trace.FireTrace(component, name='example_generator') info = inspectutils.Info(component) info['docstring_info'] = docstrings.parse(info['docstring']) help_output = helputils.HelpText(component, info, t) expected_output = """ NAME example_generator - Generators have a ``Yields`` section instead of a ``Returns`` section. SYNOPSIS example_generator N DESCRIPTION Generators have a ``Yields`` section instead of a ``Returns`` section. POSITIONAL ARGUMENTS N The upper limit of the range to generate, from 0 to `n` - 1. NOTES You could also use flags syntax for POSITIONAL ARGUMENTS """ self.assertEqual( textwrap.dedent(expected_output).lstrip('\n'), help_output)
def test_google_format_typed_args_and_returns(self): docstring = """Docstring summary. This is a longer description of the docstring. It spans multiple lines, as is allowed. Args: param1 (int): The first parameter. param2 (str): The second parameter. Returns: bool: The return value. True for success, False otherwise. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='Docstring summary.', description= 'This is a longer description of the docstring. It spans ' 'multiple lines, as\nis allowed.', args=[ ArgInfo(name='param1', type='int', description='The first parameter.'), ArgInfo(name='param2', type='str', description='The second parameter.'), ], returns='bool: The return value. True for success, False otherwise.' ) self.assertEqual(expected_docstring_info, docstring_info)
def testHelpScreenForFunctionFunctionWithDefaultArgs(self): component = tc.WithDefaults().double t = trace.FireTrace(component, name='double') info = inspectutils.Info(component) info['docstring_info'] = docstrings.parse(info['docstring']) help_output = helputils.HelpText(component, info, t) expected_output = """ NAME double - Returns the input multiplied by 2. SYNOPSIS double [--count=COUNT] DESCRIPTION Returns the input multiplied by 2. FLAGS --count Input number that you want to double. NOTES You could also use flags syntax for POSITIONAL ARGUMENTS """ self.assertEqual( textwrap.dedent(expected_output).lstrip('\n'), help_output)
def test_one_line_simple(self): docstring = """A simple one line docstring.""" docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary="A simple one line docstring.", ) self.assertEqual(expected_docstring_info, docstring_info)
def test_numpy_format_typed_args_and_returns(self): docstring = """Docstring summary. This is a longer description of the docstring. It spans across multiple lines. Parameters ---------- param1 : int The first parameter. param2 : str The second parameter. Returns ------- bool True if successful, False otherwise. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary="Docstring summary.", description="This is a longer description of the docstring. It spans " "across multiple\nlines.", args=[ ArgInfo(name="param1", type="int", description="The first parameter."), ArgInfo(name="param2", type="str", description="The second parameter."), ], # TODO(dbieber): Support return type. returns="bool True if successful, False otherwise.", ) self.assertEqual(expected_docstring_info, docstring_info)
def test_numpy_format_multiline_arg_description(self): docstring = """Docstring summary. This is a longer description of the docstring. It spans across multiple lines. Parameters ---------- param1 : int The first parameter. param2 : str The second parameter. This has a lot of text, enough to cover two lines. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary="Docstring summary.", description="This is a longer description of the docstring. It spans " "across multiple\nlines.", args=[ ArgInfo(name="param1", type="int", description="The first parameter."), ArgInfo( name="param2", type="str", description="The second parameter. This has a lot of text, " "enough to cover two lines.", ), ], ) self.assertEqual(expected_docstring_info, docstring_info)
def test_rst_format_typed_args_and_returns(self): docstring = """Docstring summary. This is a longer description of the docstring. It spans across multiple lines. :param arg1: Description of arg1. :type arg1: str. :param arg2: Description of arg2. :type arg2: bool. :returns: int -- description of the return value. :raises: AttributeError, KeyError """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary="Docstring summary.", description="This is a longer description of the docstring. It spans " "across multiple\nlines.", args=[ ArgInfo(name="arg1", type="str", description="Description of arg1."), ArgInfo(name="arg2", type="bool", description="Description of arg2."), ], returns="int -- description of the return value.", raises="AttributeError, KeyError", ) self.assertEqual(expected_docstring_info, docstring_info)
def test_google_format_multiline_arg_description(self): docstring = """Docstring summary. This is a longer description of the docstring. It spans multiple lines, as is allowed. Args: param1 (int): The first parameter. param2 (str): The second parameter. This has a lot of text, enough to cover two lines. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary="Docstring summary.", description="This is a longer description of the docstring. It spans " "multiple lines, as\nis allowed.", args=[ ArgInfo(name="param1", type="int", description="The first parameter."), ArgInfo( name="param2", type="str", description="The second parameter. This has a lot of text, " "enough to cover two lines.", ), ], ) self.assertEqual(expected_docstring_info, docstring_info)
def testHelpScreen(self): component = tc.ClassWithDocstring() t = trace.FireTrace(component, name='ClassWithDocstring') info = inspectutils.Info(component) info['docstring_info'] = docstrings.parse(info['docstring']) help_output = helptext.HelpText(component, info, t) expected_output = """ NAME ClassWithDocstring - Test class for testing help text output. SYNOPSIS ClassWithDocstring COMMAND | VALUES DESCRIPTION This is some detail description of this test class. COMMANDS COMMAND is one of the followings: print_msg Prints a message. VALUES VALUE is one of the followings: message The default message to print. """ self.assertEqual(textwrap.dedent(expected_output).lstrip('\n'), help_output)
def test_rst_format_typed_args_and_returns(self): docstring = """Docstring summary. This is a longer description of the docstring. It spans across multiple lines. :param arg1: Description of arg1. :type arg1: str. :param arg2: Description of arg2. :type arg2: bool. :returns: int -- description of the return value. :raises: AttributeError, KeyError """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='Docstring summary.', description= 'This is a longer description of the docstring. It spans ' 'across multiple\nlines.', args=[ ArgInfo(name='arg1', type='str', description='Description of arg1.'), ArgInfo(name='arg2', type='bool', description='Description of arg2.'), ], returns='int -- description of the return value.', raises='AttributeError, KeyError', ) self.assertEqual(expected_docstring_info, docstring_info)
def test_google_format_arg_named_args(self): docstring = """ Args: args: arg_description """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo(args=[ ArgInfo(name='args', description='arg_description'), ]) self.assertEqual(expected_docstring_info, docstring_info)
def test_one_line_runs_over_whitespace(self): docstring = """ A one line docstring thats both a little too verbose and a little too long so it runs onto a second line. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='A one line docstring thats both a little too verbose and ' 'a little too long so it runs onto a second line.', ) self.assertEqual(expected_docstring_info, docstring_info)
def test_one_line_runs_over(self): # pylint: disable=line-too-long docstring = """A one line docstring thats both a little too verbose and a little too long so it runs onto a second line. """ # pylint: enable=line-too-long docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='A one line docstring thats both a little too verbose and ' 'a little too long so it runs onto a second line.', ) self.assertEqual(expected_docstring_info, docstring_info)
def test_one_line_too_long(self): # pylint: disable=line-too-long docstring = """A one line docstring thats both a little too verbose and a little too long so it keeps going well beyond a reasonable length for a one-liner. """ # pylint: enable=line-too-long docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='A one line docstring thats both a little too verbose and ' 'a little too long so it keeps going well beyond a reasonable length ' 'for a one-liner.', ) self.assertEqual(expected_docstring_info, docstring_info)
def test_google_section_with_blank_first_line(self): docstring = """Inspired by requests HTTPAdapter docstring. :param x: Simple param. Usage: >>> import requests """ docstring_info = docstrings.parse(docstring) self.assertEqual('Inspired by requests HTTPAdapter docstring.', docstring_info.summary)
def testHelpTextNoDefaults(self): component = tc.NoDefaults # TODO(joejoevictor): We should have inspectutils.Info to generate # info['docstring_info'] as well. info = inspectutils.Info(component) info['docstring_info'] = docstrings.parse(info['docstring']) help_screen = helptext.HelpText( component=component, info=info, trace=trace.FireTrace(component, name='NoDefaults')) self.assertIn('NAME\n NoDefaults', help_screen) self.assertIn('SYNOPSIS\n NoDefaults', help_screen) self.assertNotIn('DESCRIPTION', help_screen) self.assertNotIn('NOTES', help_screen)
def test_google_format_args_only(self): docstring = """One line description. Args: arg1: arg1_description arg2: arg2_description """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='One line description.', args=[ ArgInfo(name='arg1', description='arg1_description'), ArgInfo(name='arg2', description='arg2_description'), ]) self.assertEqual(expected_docstring_info, docstring_info)
def testHelpTextNoDefaultsObject(self): component = tc.NoDefaults() info = inspectutils.Info(component) info['docstring_info'] = docstrings.parse(info['docstring']) help_screen = helptext.HelpText( component=component, info=info, trace=trace.FireTrace(component, name='NoDefaults')) self.assertIn('NAME\n NoDefaults', help_screen) self.assertIn('SYNOPSIS\n NoDefaults COMMAND', help_screen) self.assertNotIn('DESCRIPTION', help_screen) self.assertIn('COMMANDS\n COMMAND is one of the followings:', help_screen) self.assertIn('double', help_screen) self.assertIn('triple', help_screen) self.assertNotIn('NOTES', help_screen)
def HelpString(component, trace=None, verbose=False): """Returns a help string for a supplied component. The component can be any Python class, object, function, module, etc. Args: component: The component to determine the help string for. trace: The Fire trace leading to this component. verbose: Whether to include private members in the help string. Returns: String suitable for display giving information about the component. """ info = inspectutils.Info(component) info['usage'] = UsageString(component, trace, verbose) info['docstring_info'] = docstrings.parse(info['docstring']) return _HelpText(info, trace)
def test_multisection_docstring(self): docstring = """Docstring summary. This is the first section of a docstring description. This is the second section of a docstring description. This docstring description has just two sections. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='Docstring summary.', description='This is the first section of a docstring description.' '\n\n' 'This is the second section of a docstring description. This docstring' '\n' 'description has just two sections.', ) self.assertEqual(expected_docstring_info, docstring_info)
def Info(component): """Returns a dict with information about the given component. The dict will have at least some of the following fields. type_name: The type of `component`. string_form: A string representation of `component`. file: The file in which `component` is defined. line: The line number at which `component` is defined. docstring: The docstring of `component`. init_docstring: The init docstring of `component`. class_docstring: The class docstring of `component`. call_docstring: The call docstring of `component`. length: The length of `component`. Args: component: The component to analyze. Returns: A dict with information about the component. """ try: from IPython.core import ( oinspect, ) # pylint: disable=import-outside-toplevel,g-import-not-at-top inspector = oinspect.Inspector() info = inspector.info(component) # IPython's oinspect.Inspector.info may return '<no docstring>' if info["docstring"] == "<no docstring>": info["docstring"] = None except ImportError: info = _InfoBackup(component) try: unused_code, lineindex = inspect.findsource(component) info["line"] = lineindex + 1 except (TypeError, IOError): info["line"] = None if "docstring" in info: info["docstring_info"] = docstrings.parse(info["docstring"]) return info
def test_rst_format_typed_args_and_kwargs(self): docstring = """Docstring summary. :param arg1: Description of arg1. :type arg1: str. :key arg2: Description of arg2. :type arg2: bool. :key arg3: Description of arg3. :type arg3: str. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary="Docstring summary.", args=[ ArgInfo(name="arg1", type="str", description="Description of arg1."), KwargInfo(name="arg2", type="bool", description="Description of arg2."), KwargInfo(name="arg3", type="str", description="Description of arg3."), ], ) self.assertEqual(expected_docstring_info, docstring_info)
def test_numpy_colon_in_description(self): docstring = """ Greets name. Arguments --------- name : str name, default : World arg2 : int arg2, default:None arg3 : bool """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary="Greets name.", description=None, args=[ ArgInfo(name="name", type="str", description="name, default : World"), ArgInfo(name="arg2", type="int", description="arg2, default:None"), ArgInfo(name="arg3", type="bool", description=None), ], ) self.assertEqual(expected_docstring_info, docstring_info)
def HelpString(component, trace=None, verbose=False): """Returns the text to show for a supplied component. The component can be any Python class, object, function, module, etc. Args: component: The component to determine the help string for. trace: The Fire trace leading to this component. verbose: Whether to include private members in the help string. Returns: String suitable for display giving information about the component. """ info = inspectutils.Info(component) info['docstring_info'] = docstrings.parse(info['docstring']) is_error_screen = False if trace: is_error_screen = trace.HasError() if is_error_screen: return UsageText(component, info, trace, verbose=verbose) else: return HelpText(component, info, trace, verbose=verbose)
def HelpTextForObject(component, info, trace=None, verbose=False): """Generates help text for python objects. Args: component: Current component to generate help text for. info: Info containing metadata of component. trace: FireTrace object that leads to current component. verbose: Whether to display help text in verbose mode. Returns: Formatted help text for display. """ current_command = GetCurrentCommand(trace) docstring_info = info['docstring_info'] command_summary = docstring_info.summary if docstring_info.summary else '' if docstring_info.description: command_description = docstring_info.description else: command_description = '' groups = [] commands = [] values = [] members = completion._Members(component, verbose) # pylint: disable=protected-access for member_name, member in members: if value_types.IsGroup(member): groups.append((member_name, member)) if value_types.IsCommand(member): commands.append((member_name, member)) if value_types.IsValue(member): values.append((member_name, member)) usage_details_sections = [] possible_actions = [] # TODO(joejoevictor): Add global flags to here. Also, if it's a callable, # there will be additional flags. possible_flags = '' if groups: # TODO(joejoevictor): Add missing GROUPS section handling possible_actions.append('GROUP') if commands: possible_actions.append('COMMAND') command_item_strings = [] for command_name, command in commands: command_docstring_info = docstrings.parse( inspectutils.Info(command)['docstring']) command_item_strings.append( _CreateItem(command_name, command_docstring_info.summary)) usage_details_sections.append( ('COMMANDS', _NewChoicesSection('COMMAND', command_item_strings))) if values: possible_actions.append('VALUE') value_item_strings = [] for value_name, value in values: del value init_docstring_info = docstrings.parse( inspectutils.Info(component.__class__.__init__)['docstring']) for arg_info in init_docstring_info.args: if arg_info.name == value_name: value_item_strings.append( _CreateItem(value_name, arg_info.description)) usage_details_sections.append( ('VALUES', _NewChoicesSection('VALUE', value_item_strings))) possible_actions_string = ' ' + (' | '.join(possible_actions)) synopsis_template = '{current_command}{possible_actions}{possible_flags}' synopsis_string = synopsis_template.format( current_command=current_command, possible_actions=possible_actions_string, possible_flags=possible_flags) description_sections = [] if command_description: description_sections.append(('DESCRIPTION', command_description)) name_line = '{current_command} - {command_summary}'.format( current_command=current_command, command_summary=command_summary) output_sections = [ ('NAME', name_line), ('SYNOPSIS', synopsis_string), ] + description_sections + usage_details_sections return '\n\n'.join( _CreateOutputSection(name, content) for name, content in output_sections)
def HelpTextForObject(component, info, trace=None, verbose=False): """Generates help text for python objects. Args: component: Current component to generate help text for. info: Info containing metadata of component. trace: FireTrace object that leads to current component. verbose: Whether to display help text in verbose mode. Returns: Formatted help text for display. """ output_template = """NAME {current_command} - {command_summary} SYNOPSIS {synopsis} DESCRIPTION {command_description} {detail_section} """ current_command = GetCurrentCommand(trace) docstring_info = info['docstring_info'] command_summary = docstring_info.summary if docstring_info.summary else '' if docstring_info.description: command_description = docstring_info.description else: command_description = '' groups = [] commands = [] values = [] members = completion._Members(component, verbose) # pylint: disable=protected-access for member_name, member in members: if value_types.IsGroup(member): groups.append((member_name, member)) if value_types.IsCommand(member): commands.append((member_name, member)) if value_types.IsValue(member): values.append((member_name, member)) possible_actions = [] # TODO(joejoevictor): Add global flags to here. Also, if it's a callable, # there will be additional flags. possible_flags = '' detail_section_string = '' item_template = """ {name} {command_summary} """ if groups: # TODO(joejoevictor): Add missing GROUPS section handling possible_actions.append('GROUP') if commands: possible_actions.append('COMMAND') commands_str_template = """ COMMANDS COMMAND is one of the followings: {items} """ command_item_strings = [] for command_name, command in commands: command_docstring_info = docstrings.parse( inspectutils.Info(command)['docstring']) command_item_strings.append( item_template.format( name=command_name, command_summary=command_docstring_info.summary)) detail_section_string += commands_str_template.format( items=('\n'.join(command_item_strings)).rstrip('\n')) if values: possible_actions.append('VALUES') values_str_template = """ VALUES VALUE is one of the followings: {items} """ value_item_strings = [] for value_name, value in values: del value init_docstring_info = docstrings.parse( inspectutils.Info(component.__class__.__init__)['docstring']) for arg_info in init_docstring_info.args: if arg_info.name == value_name: value_item_strings.append( item_template.format( name=value_name, command_summary=arg_info.description)) detail_section_string += values_str_template.format( items=('\n'.join(value_item_strings)).rstrip('\n')) possible_actions_string = ' ' + (' | '.join(possible_actions)) synopsis_template = '{current_command}{possible_actions}{possible_flags}' synopsis_string = synopsis_template.format( current_command=current_command, possible_actions=possible_actions_string, possible_flags=possible_flags) return output_template.format(current_command=current_command, command_summary=command_summary, synopsis=synopsis_string, command_description=command_description, detail_section=detail_section_string)
def test_fuzz_parse(self, value): docstrings.parse(value)