def _GetArgsAndFlagsString(spec, metadata): """The args and flags string for showing how to call a function. If positional arguments are accepted, the args will be shown as positional. E.g. "ARG1 ARG2 [--flag=FLAG]" If positional arguments are disallowed, the args will be shown with flags syntax. E.g. "--arg1=ARG1 [--flag=FLAG]" Args: spec: The full arg spec for the component to construct the args and flags string for. metadata: Metadata for the component, including whether it accepts positional arguments. Returns: The constructed args and flags string. """ args_with_no_defaults = spec.args[:len(spec.args) - len(spec.defaults)] args_with_defaults = spec.args[len(spec.args) - len(spec.defaults):] # Check if positional args are allowed. If not, require flag syntax for args. accepts_positional_args = metadata.get(decorators.ACCEPTS_POSITIONAL_ARGS) arg_and_flag_strings = [] if args_with_no_defaults: if accepts_positional_args: arg_strings = [ formatting.Underline(arg.upper()) for arg in args_with_no_defaults ] else: arg_strings = [ '--{arg}={arg_upper}'.format(arg=arg, arg_upper=formatting.Underline( arg.upper())) for arg in args_with_no_defaults ] arg_and_flag_strings.extend(arg_strings) # If there are any arguments that are treated as flags: if args_with_defaults or spec.kwonlyargs or spec.varkw: arg_and_flag_strings.append('<flags>') if spec.varargs: varargs_string = '[{varargs}]...'.format( varargs=formatting.Underline(spec.varargs.upper())) arg_and_flag_strings.append(varargs_string) return ' '.join(arg_and_flag_strings)
def _NewChoicesSection(name, choices): return _CreateItem( "{name} is one of the following:".format( name=formatting.Bold(formatting.Underline(name))), "\n" + "\n\n".join(choices), indent=1, )
def _CreateFlagItem(flag, docstring_info, spec, required=False, flag_string=None): """Returns a string describing a flag using docstring and FullArgSpec info. Args: flag: The name of the flag. docstring_info: A docstrings.DocstringInfo namedtuple with information about the containing function's docstring. spec: An instance of fire.inspectutils.FullArgSpec, containing type and default information about the arguments to a callable. required: Whether the flag is required. flag_string: If provided, use this string for the flag, rather than constructing one from the flag name. Returns: A string to be used in constructing the help screen for the function. """ # pylint: disable=g-bad-todo # TODO(MichaelCG8): Get type and default information from docstrings if it is # not available in FullArgSpec. This will require updating # fire.docstrings.parser(). # The help string is indented, so calculate the maximum permitted length # before indentation to avoid exceeding the maximum line length. max_str_length = LINE_LENGTH - SECTION_INDENTATION - SUBSECTION_INDENTATION description = _GetArgDescription(flag, docstring_info) if not flag_string: flag_string_template = "--{flag_name}={flag_name_upper}" flag_string = flag_string_template.format( flag_name=flag, flag_name_upper=formatting.Underline(flag.upper())) if required: flag_string += " (required)" arg_type = _GetArgType(flag, spec) arg_default = _GetArgDefault(flag, spec) # We need to handle the case where there is a default of None, but otherwise # the argument has another type. if arg_default == "None": arg_type = "Optional[{}]".format(arg_type) arg_type = "Type: {}".format(arg_type) if arg_type else "" available_space = max_str_length - len(arg_type) arg_type = formatting.EllipsisTruncate(arg_type, available_space, max_str_length) arg_default = "Default: {}".format(arg_default) if arg_default else "" available_space = max_str_length - len(arg_default) arg_default = formatting.EllipsisTruncate(arg_default, available_space, max_str_length) description = "\n".join(part for part in (arg_type, arg_default, description) if part) return _CreateItem(flag_string, description, indent=SUBSECTION_INDENTATION)
def testHelpTextUnderlineFlag(self): component = tc.WithDefaults().triple t = trace.FireTrace(component, name="triple") help_screen = helptext.HelpText(component, t) self.assertIn(formatting.Bold("NAME") + "\n triple", help_screen) self.assertIn(formatting.Bold("SYNOPSIS") + "\n triple <flags>", help_screen) self.assertIn( formatting.Bold("FLAGS") + "\n --" + formatting.Underline("count"), help_screen, )
def testHelpTextUnderlineFlag(self): component = tc.WithDefaults().triple t = trace.FireTrace(component, name='triple') help_screen = helptext.HelpText(component, t) self.assertIn(formatting.Bold('NAME') + '\n triple', help_screen) self.assertIn( formatting.Bold('SYNOPSIS') + '\n triple <flags>', help_screen) self.assertIn( formatting.Bold('FLAGS') + '\n --' + formatting.Underline('count'), help_screen)
def _CreateFlagItem(flag, docstring_info, spec, required=False): """Returns a string describing a flag using docstring and FullArgSpec info. Args: flag: The name of the flag. docstring_info: A docstrings.DocstringInfo namedtuple with information about the containing function's docstring. spec: An instance of fire.inspectutils.FullArgSpec, containing type and default information about the arguments to a callable. required: Whether the flag is required. Returns: A string to be used in constructing the help screen for the function. """ # TODO(MichaelCG8): In future it would be good to be able to get type and # default information out of docstrings if it is not available in # FullArgSpec. This would require updating fire.docstrings.parser() and a # decision would need to be made about which takes priority if the docstrings # and function definition disagree. # The help string is indented, so calculate the maximum permitted length # before indentation to avoid exceeding the maximum line length. max_str_length = LINE_LENGTH - SECTION_INDENTATION - SUBSECTION_INDENTATION description = _GetArgDescription(flag, docstring_info) flag_string_template = '--{flag_name}={flag_name_upper}' flag_string = flag_string_template.format( flag_name=flag, flag_name_upper=formatting.Underline(flag.upper())) if required: flag_string += ' (required)' arg_type = _GetArgType(flag, spec) arg_default = _GetArgDefault(flag, spec) # We need to handle the case where there is a default # of None, but otherwise the argument has another type. if arg_default == 'None': arg_type = 'Optional[{}]'.format(arg_type) arg_type = 'Type: {}'.format(arg_type) if arg_type else '' available_space = max_str_length - len(arg_type) arg_type = \ formatting.EllipsisTruncate(arg_type, available_space, max_str_length) arg_default = 'Default: {}'.format(arg_default) if arg_default else '' available_space = max_str_length - len(arg_default) arg_default = \ formatting.EllipsisTruncate(arg_default, available_space, max_str_length) description = '\n'.join(part for part in (arg_type, arg_default, description) if part) return _CreateItem(flag_string, description, indent=SUBSECTION_INDENTATION)
def testHelpTextUnderlineFlag(self): component = tc.WithDefaults().triple info = inspectutils.Info(component) t = trace.FireTrace(component, name='triple') help_screen = helptext.HelpText(component, info, t) self.assertIn(formatting.Bold('NAME') + '\n triple', help_screen) self.assertIn( formatting.Bold('SYNOPSIS') + '\n triple [--count=COUNT]', help_screen) self.assertIn( formatting.Bold('FLAGS') + '\n --' + formatting.Underline('count'), help_screen)
def _GetPossibleActionsString(actions_grouped_by_kind): """A help screen string listing the possible action kinds available.""" groups, commands, values, indexes = actions_grouped_by_kind possible_actions = [] if groups: possible_actions.append('GROUP') if commands: possible_actions.append('COMMAND') if values: possible_actions.append('VALUE') if indexes: possible_actions.append('INDEX') possible_actions_string = ' | '.join( formatting.Underline(action) for action in possible_actions) return possible_actions_string
def _CreateFlagItem(flag, docstring_info, required=False): """Returns a string describing a flag using information from the docstring. Args: flag: The name of the flag. docstring_info: A docstrings.DocstringInfo namedtuple with information about the containing function's docstring. required: Whether the flag is required. Returns: A string to be used in constructing the help screen for the function. """ description = _GetArgDescription(flag, docstring_info) flag_string_template = '--{flag_name}={flag_name_upper}' flag = flag_string_template.format( flag_name=flag, flag_name_upper=formatting.Underline(flag.upper())) if required: flag += ' (required)' return _CreateItem(flag, description, indent=4)
def _CreateFlagItem(flag, docstring_info): """Returns a string describing a flag using information from the docstring. Args: flag: The name of the flag. docstring_info: A docstrings.DocstringInfo namedtuple with information about the containing function's docstring. Returns: A string to be used in constructing the help screen for the function. """ description = None if docstring_info.args: for arg_in_docstring in docstring_info.args: if arg_in_docstring.name == flag: description = arg_in_docstring.description break flag = '--{flag}'.format(flag=formatting.Underline(flag)) if description: return _CreateItem(flag, description, indent=2) return flag
def _NewChoicesSection(name, choices): return _CreateItem( '{name} is one of the following:'.format( name=formatting.Bold(formatting.Underline(name))), '\n' + '\n\n'.join(choices), indent=1)
def test_underline(self): text = formatting.Underline('hello') self.assertEqual('\x1b[4mhello\x1b[0m', text)
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) current_command_without_separator = GetCurrentCommand( trace, include_separators=False) 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: possible_actions.append('GROUP') usage_details_section = GroupUsageDetailsSection(groups) usage_details_sections.append(usage_details_section) if commands: possible_actions.append('COMMAND') usage_details_section = CommandUsageDetailsSection(commands) usage_details_sections.append(usage_details_section) if values: possible_actions.append('VALUE') usage_details_section = ValuesUsageDetailsSection(component, values) usage_details_sections.append(usage_details_section) 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_without_separator, 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 HelpTextForFunction(component, info, trace=None, verbose=False): """Returns detail help text for a function component. 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. """ # TODO(joejoevictor): Implement verbose related output del verbose current_command = GetCurrentCommand(trace) current_command_without_separator = GetCurrentCommand( trace, include_separators=False) summary, description = GetSummaryAndDescription(info['docstring_info']) args_with_no_defaults, args_with_defaults, flags = GetArgsAngFlags( component) del args_with_defaults # Name section name_section_template = '{current_command}{command_summary}' command_summary_str = ' - ' + summary if summary else '' name_section = name_section_template.format( current_command=current_command_without_separator, command_summary=command_summary_str) # Check if positional args are allowed. If not, require flag syntax for args. metadata = decorators.GetMetadata(component) accepts_positional_args = metadata.get(decorators.ACCEPTS_POSITIONAL_ARGS) arg_and_flag_strings = [] if args_with_no_defaults: if accepts_positional_args: arg_strings = [ formatting.Underline(arg.upper()) for arg in args_with_no_defaults ] else: arg_strings = [ '--{arg}={arg_upper}'.format(arg=arg, arg_upper=formatting.Underline( arg.upper())) for arg in args_with_no_defaults ] arg_and_flag_strings.extend(arg_strings) flag_string_template = '[--{flag_name}={flag_name_upper}]' if flags: flag_strings = [ flag_string_template.format(flag_name=formatting.Underline(flag), flag_name_upper=flag.upper()) for flag in flags ] arg_and_flag_strings.extend(flag_strings) args_and_flags = ' '.join(arg_and_flag_strings) # Synopsis section synopsis_section_template = '{current_command} {args_and_flags}' synopsis_section = synopsis_section_template.format( current_command=current_command, args_and_flags=args_and_flags) # Description section command_description = GetDescriptionSectionText(summary, description) description_sections = [] if command_description: description_sections.append(('DESCRIPTION', command_description)) # Positional arguments and flags section docstring_info = info['docstring_info'] args_and_flags_sections = [] notes_sections = [] arg_items = [ _CreateArgItem(arg, docstring_info) for arg in args_with_no_defaults ] if arg_items: title = 'POSITIONAL ARGUMENTS' if accepts_positional_args else 'ARGUMENTS' arguments_section = (title, '\n'.join(arg_items).rstrip('\n')) args_and_flags_sections.append(arguments_section) if accepts_positional_args: notes_sections.append( ('NOTES', 'You can also use flags syntax for POSITIONAL ARGUMENTS')) flag_items = [_CreateFlagItem(flag, docstring_info) for flag in flags] if flag_items: flags_section = ('FLAGS', '\n'.join(flag_items)) args_and_flags_sections.append(flags_section) output_sections = [ ('NAME', name_section), ('SYNOPSIS', synopsis_section), ] + description_sections + args_and_flags_sections + notes_sections return '\n\n'.join( _CreateOutputSection(name, content) for name, content in output_sections)
def _GetPossibleActionsString(possible_actions): """A help screen string listing the possible action kinds available.""" return ' | '.join(formatting.Underline(action.upper()) for action in possible_actions)
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 HelpTextForFunction(component, info, trace=None, verbose=False): """Returns detail help text for a function component. 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. """ # TODO(joejoevictor): Implement verbose related output del verbose current_command = GetCurrentCommand(trace) summary, description = GetSummaryAndDescription(info['docstring_info']) spec = inspectutils.GetFullArgSpec(component) args = spec.args args_with_no_defaults, args_with_defaults, flags = GetArgsAngFlags( component) del args_with_defaults # Name section name_section_template = '{current_command}{command_summary}' command_summary_str = ' - ' + summary if summary else '' name_section = name_section_template.format( current_command=current_command, command_summary=command_summary_str) args_and_flags = '' if args_with_no_defaults: items = [ formatting.Underline(arg.upper()) for arg in args_with_no_defaults ] args_and_flags = ' '.join(items) synopsis_flag_template = '[--{flag_name}={flag_name_upper}]' if flags: items = [ synopsis_flag_template.format(flag_name=formatting.Underline(flag), flag_name_upper=flag.upper()) for flag in flags ] args_and_flags = args_and_flags + ' '.join(items) # Synopsis section synopsis_section_template = '{current_command} {args_and_flags}' positional_arguments = '|'.join(args) if positional_arguments: positional_arguments = ' ' + positional_arguments synopsis_section = synopsis_section_template.format( current_command=current_command, args_and_flags=args_and_flags) # Description section command_description = GetDescriptionSectionText(summary, description) description_sections = [] if command_description: description_sections.append(('DESCRIPTION', command_description)) # Positional arguments and flags section docstring_info = info['docstring_info'] args_and_flags_sections = [] notes_sections = [] pos_arg_items = [] pos_arg_items = [ _CreatePositionalArgItem(arg, docstring_info) for arg in args_with_no_defaults ] if pos_arg_items: positional_arguments_section = ('POSITIONAL ARGUMENTS', '\n'.join(pos_arg_items).rstrip('\n')) args_and_flags_sections.append(positional_arguments_section) notes_sections.append( ('NOTES', 'You could also use flags syntax for POSITIONAL ARGUMENTS')) flag_items = [_CreateFlagItem(flag, docstring_info) for flag in flags] if flag_items: flags_section = ('FLAGS', '\n'.join(flag_items)) args_and_flags_sections.append(flags_section) output_sections = [ ('NAME', name_section), ('SYNOPSIS', synopsis_section), ] + description_sections + args_and_flags_sections + notes_sections return '\n\n'.join( _CreateOutputSection(name, content) for name, content in output_sections)
def test_underline(self): text = formatting.Underline("hello") self.assertEqual("\x1b[4mhello\x1b[0m", text)