def testUsageOutputMethod(self): component = tc.NoDefaults().double t = trace.FireTrace(component, name='NoDefaults') t.AddAccessedProperty(component, 'double', ['double'], None, None) info = inspectutils.Info(component) usage_output = helptext.UsageText(component, info, trace=t, verbose=True) expected_output = ''' Usage: NoDefaults double COUNT For detailed information on this command, run: NoDefaults double --help ''' self.assertEqual(usage_output, textwrap.dedent(expected_output).lstrip('\n'))
def testUsageOutput(self): component = tc.NoDefaults() t = trace.FireTrace(component, name='NoDefaults') info = inspectutils.Info(component) usage_output = helptext.UsageText(component, info, trace=t, verbose=False) expected_output = ''' Usage: NoDefaults <command> available commands: double | triple For detailed information on this command, run: NoDefaults --help''' self.assertEqual(usage_output, textwrap.dedent(expected_output).lstrip('\n'))
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 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) fields = [ 'type_name', 'string_form', 'file', 'line', 'docstring', 'init_docstring', 'class_docstring', 'call_docstring', 'length', 'usage', ] max_size = max( len(_NormalizeField(field)) + 1 for field in fields if field in info and info[field]) format_string = '{{field:{max_size}s}} {{value}}'.format(max_size=max_size) lines = [] for field in fields: value = _DisplayValue(info, field, padding=max_size + 1) if value: if lines and field == 'usage': lines.append('') # Ensure a blank line before usage. lines.append( format_string.format( field=_NormalizeField(field) + ':', value=value, )) return '\n'.join(lines)
def _ValuesUsageDetailsSection(component, values): """Creates a section tuple for the values section of the usage details.""" value_item_strings = [] for value_name, value in values.GetItems(): del value init_info = inspectutils.Info(component.__class__.__init__) value_item = None if 'docstring_info' in init_info: init_docstring_info = init_info['docstring_info'] if init_docstring_info.args: for arg_info in init_docstring_info.args: if arg_info.name == value_name: value_item = _CreateItem(value_name, arg_info.description) if value_item is None: value_item = str(value_name) value_item_strings.append(value_item) return ('VALUES', _NewChoicesSection('VALUE', value_item_strings))
def testHelpTextObjectWithGroupAndValues(self): component = tc.TypedProperties() t = trace.FireTrace(component, name='TypedProperties') info = inspectutils.Info(component) help_screen = helptext.HelpText(component=component, info=info, trace=t, verbose=True) print(help_screen) self.assertIn('GROUPS', help_screen) self.assertIn('GROUP is one of the following:', help_screen) self.assertIn( 'charlie\n Class with functions that have default arguments.', help_screen) self.assertIn('VALUES', help_screen) self.assertIn('VALUE is one of the following:', help_screen) self.assertIn('alpha', help_screen)
def testUsageOutputFunctionWithHelp(self): component = tc.function_with_help t = trace.FireTrace(component, name='function_with_help') info = inspectutils.Info(component) usage_output = helptext.UsageText(component, info, trace=t, verbose=True) expected_output = ''' Usage: function_with_help <flags> Available flags: --help For detailed information on this command, run: function_with_help -- --help ''' self.assertEqual(usage_output, textwrap.dedent(expected_output).lstrip('\n'))
def testUsageOutputFunctionWithDocstring(self): component = tc.multiplier_with_docstring t = trace.FireTrace(component, name='multiplier_with_docstring') info = inspectutils.Info(component) usage_output = helptext.UsageText(component, info, trace=t, verbose=True) expected_output = ''' Usage: multiplier_with_docstring NUM <flags> Available flags: --rate For detailed information on this command, run: multiplier_with_docstring --help ''' self.assertEqual(usage_output, textwrap.dedent(expected_output).lstrip('\n'))
def HelpText(component, trace=None, verbose=False): """Gets the help string for the current component, suitable for a help screen. Args: component: The component to construct the help string for. trace: The Fire trace of the command so far. The command executed so far can be extracted from this trace. verbose: Whether to include private members in the help screen. Returns: The full help screen as a string. """ # Preprocessing needed to create the sections: info = inspectutils.Info(component) actions_grouped_by_kind = _GetActionsGroupedByKind(component, verbose=verbose) spec = inspectutils.GetFullArgSpec(component) metadata = decorators.GetMetadata(component) # Sections: name_section = _NameSection(component, info, trace=trace, verbose=verbose) synopsis_section = _SynopsisSection( component, actions_grouped_by_kind, spec, metadata, trace=trace) description_section = _DescriptionSection(component, info) # TODO(dbieber): Add returns and raises sections for functions. if callable(component): args_and_flags_sections, notes_sections = _ArgsAndFlagsSections( info, spec, metadata) else: args_and_flags_sections = [] notes_sections = [] usage_details_sections = _UsageDetailsSections(component, actions_grouped_by_kind) sections = ( [name_section, synopsis_section, description_section] + args_and_flags_sections + usage_details_sections + notes_sections ) return '\n\n'.join( _CreateOutputSection(*section) for section in sections if section is not None )
def testHelpTextShortList(self): component = [10] info = inspectutils.Info(component) help_screen = helptext.HelpText(component=component, info=info, trace=trace.FireTrace( component, 'list')) self.assertIn('NAME\n list', help_screen) self.assertIn('SYNOPSIS\n list COMMAND', help_screen) # We don't check description content here since the content could be python # version dependent. self.assertIn('DESCRIPTION\n', help_screen) # We don't check the listed commands comprehensively since the list API # could potentially change between Python versions. Check a few # functions(command) that we're confident likely remain available. self.assertIn('COMMANDS\n COMMAND is one of the followings:\n', help_screen) self.assertIn(' append\n', help_screen)
def testUsageOutputCallable(self): # This is both a group and a command! component = tc.CallableWithKeywordArgument t = trace.FireTrace(component, name='CallableWithKeywordArgument') info = inspectutils.Info(component) usage_output = helptext.UsageText(component, info, trace=t, verbose=True) # TODO(zuhaohen): We need to handle the case for keyword args as well # i.e. __call__ method of CallableWithKeywordArgument expected_output = ''' Usage: CallableWithKeywordArgument <command> Available commands: print_msg For detailed information on this command, run: CallableWithKeywordArgument -- --help ''' self.assertEqual( usage_output, textwrap.dedent(expected_output).lstrip('\n'))
def _MakeUsageDetailsSection(action_group): """Creates a usage details section for the provided action group.""" item_strings = [] for name, member in action_group.GetItems(): info = inspectutils.Info(member) item = name docstring_info = info.get('docstring_info') if (docstring_info and not custom_descriptions.NeedsCustomDescription(member)): summary = docstring_info.summary elif custom_descriptions.NeedsCustomDescription(member): summary = custom_descriptions.GetSummary( member, LINE_LENGTH - SECTION_INDENTATION, LINE_LENGTH) else: summary = None item = _CreateItem(name, summary) item_strings.append(item) return (action_group.plural.upper(), _NewChoicesSection(action_group.name.upper(), item_strings))
def testHelpScreenForFunctionFunctionWithDefaultArgs(self): component = tc.WithDefaults().double t = trace.FireTrace(component, name='double') info = inspectutils.Info(component) help_output = helptext.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.""" self.assertEqual( textwrap.dedent(expected_output).strip(), help_output.strip())
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) 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 testHelpScreenForFunctionDocstringWithLineBreak(self): component = tc.ClassWithMultilineDocstring.example_generator t = trace.FireTrace(component, name='example_generator') info = inspectutils.Info(component) help_output = helptext.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).strip(), help_output.strip())
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 = helptext.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 testInfoOne(self): info = inspectutils.Info(1) self.assertEqual(info.get("type_name"), "int") self.assertEqual(info.get("file"), None) self.assertEqual(info.get("line"), None) self.assertEqual(info.get("string_form"), "1")
def testInfoClass(self): info = inspectutils.Info(tc.NoDefaults) self.assertEqual(info.get("type_name"), "type") self.assertIn(os.path.join("fire", "test_components.py"), info.get("file")) self.assertGreater(info.get("line"), 0)
def testInfoOne(self): info = inspectutils.Info(1) self.assertEqual(info.get('type_name'), 'int') self.assertEqual(info.get('file'), None) self.assertEqual(info.get('line'), None) self.assertEqual(info.get('string_form'), '1')
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 '' command_description = GetDescriptionSectionText(docstring_info.summary, docstring_info.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_info = inspectutils.Info(command) command_item = command_name if 'docstring_info' in command_info: command_docstring_info = command_info['docstring_info'] if command_docstring_info and command_docstring_info.summary: command_item = _CreateItem(command_name, command_docstring_info.summary) command_item_strings.append(command_item) 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_info = inspectutils.Info(component.__class__.__init__) value_item = value_name if 'docstring_info' in init_info: init_docstring_info = init_info['docstring_info'] for arg_info in init_docstring_info.args: if arg_info.name == value_name: value_item = _CreateItem(value_name, arg_info.description) value_item_strings.append(value_item) usage_details_sections.append( ('VALUES', _NewChoicesSection('VALUE', value_item_strings))) possible_actions_string = ' | '.join( formatting.Underline(action) for action in 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 testInfoClass(self): info = inspectutils.Info(tc.NoDefaults) self.assertEqual(info.get('type_name'), 'type') self.assertIn('fire/test_components.py', info.get('file')) self.assertGreater(info.get('line'), 0)
def HelpText(component, trace=None, verbose=False): info = inspectutils.Info(component) if inspect.isroutine(component) or inspect.isclass(component): return HelpTextForFunction(component, info, trace=trace, verbose=verbose) else: return HelpTextForObject(component, info, trace=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. """ 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 testInfoNoDocstring(self): info = inspectutils.Info(tc.NoDefaults) self.assertEqual(info['docstring'], None, 'Docstring should be None')