From 9a1c9ba2618bc3860a64e0a1af95a4d12a491bfe Mon Sep 17 00:00:00 2001 From: varunjohn786 <61010611+varunjohn786@users.noreply.github.com> Date: Fri, 8 Jan 2021 15:32:00 +0100 Subject: [PATCH 1/9] Updated code to add parameter "help_sequence". --- fire/core.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/fire/core.py b/fire/core.py index 8ca142c7..fc722056 100644 --- a/fire/core.py +++ b/fire/core.py @@ -74,11 +74,8 @@ def main(argv): from fire.console import console_io import six -if six.PY34: - import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error - -def Fire(component=None, command=None, name=None): +def Fire(component=None, command=None, name=None, help_sequence=None): """This function, Fire, is the main entrypoint for Python Fire. Executes a command either from the `command` argument or from sys.argv by @@ -158,7 +155,7 @@ def Fire(component=None, command=None, name=None): if component_trace.show_help: result = component_trace.GetResult() help_text = helptext.HelpText( - result, trace=component_trace, verbose=component_trace.verbose) + result, trace=component_trace, verbose=component_trace.verbose, help_sequence=help_sequence) output = [help_text] Display(output, out=sys.stderr) raise FireExit(0, component_trace) @@ -672,13 +669,7 @@ def _CallAndUpdateTrace(component, args, component_trace, treatment='class', fn = component.__call__ if treatment == 'callable' else component parse = _MakeParseFn(fn, metadata) (varargs, kwargs), consumed_args, remaining_args, capacity = parse(args) - - # Call the function. - if inspectutils.IsCoroutineFunction(fn): - loop = asyncio.get_event_loop() - component = loop.run_until_complete(fn(*varargs, **kwargs)) - else: - component = fn(*varargs, **kwargs) + component = fn(*varargs, **kwargs) if treatment == 'class': action = trace.INSTANTIATED_CLASS From c99b3a05f1ead7d9bbd70641268918af06f52181 Mon Sep 17 00:00:00 2001 From: varunjohn786 <61010611+varunjohn786@users.noreply.github.com> Date: Fri, 8 Jan 2021 15:34:04 +0100 Subject: [PATCH 2/9] add functionality of reordering commands updated code to add functionality to reorder commands. --- fire/helptext.py | 178 ++++++++--------------------------------------- 1 file changed, 30 insertions(+), 148 deletions(-) diff --git a/fire/helptext.py b/fire/helptext.py index b1d10b44..ecfd486c 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -34,22 +34,19 @@ from __future__ import print_function import itertools -import sys from fire import completion from fire import custom_descriptions from fire import decorators -from fire import docstrings from fire import formatting from fire import inspectutils from fire import value_types LINE_LENGTH = 80 SECTION_INDENTATION = 4 -SUBSECTION_INDENTATION = 4 -def HelpText(component, trace=None, verbose=False): +def HelpText(component, trace=None, verbose=False, help_sequence=None): """Gets the help string for the current component, suitable for a help screen. Args: @@ -81,8 +78,7 @@ def HelpText(component, trace=None, verbose=False): args_and_flags_sections = [] notes_sections = [] usage_details_sections = _UsageDetailsSections(component, - actions_grouped_by_kind) - + actions_grouped_by_kind, help_sequence=help_sequence) sections = ( [name_section, synopsis_section, description_section] + args_and_flags_sections @@ -174,7 +170,7 @@ def _DescriptionSection(component, info): def _CreateKeywordOnlyFlagItem(flag, docstring_info, spec): return _CreateFlagItem( - flag, docstring_info, spec, required=flag not in spec.kwonlydefaults) + flag, docstring_info, required=flag not in spec.kwonlydefaults) def _ArgsAndFlagsSections(info, spec, metadata): @@ -191,13 +187,13 @@ def _ArgsAndFlagsSections(info, spec, metadata): docstring_info = info['docstring_info'] arg_items = [ - _CreateArgItem(arg, docstring_info, spec) + _CreateArgItem(arg, docstring_info) for arg in args_with_no_defaults ] if spec.varargs: arg_items.append( - _CreateArgItem(spec.varargs, docstring_info, spec) + _CreateArgItem(spec.varargs, docstring_info) ) if arg_items: @@ -210,7 +206,7 @@ def _ArgsAndFlagsSections(info, spec, metadata): ) positional_flag_items = [ - _CreateFlagItem(flag, docstring_info, spec, required=False) + _CreateFlagItem(flag, docstring_info, required=False) for flag in args_with_defaults ] kwonly_flag_items = [ @@ -220,30 +216,10 @@ def _ArgsAndFlagsSections(info, spec, metadata): flag_items = positional_flag_items + kwonly_flag_items if spec.varkw: - # Include kwargs documented via :key param: - flag_string = '--{name}' - documented_kwargs = [] - for flag in docstring_info.args or []: - if isinstance(flag, docstrings.KwargInfo): - flag_item = _CreateFlagItem( - flag.name, docstring_info, spec, - flag_string=flag_string.format(name=flag.name)) - documented_kwargs.append(flag_item) - if documented_kwargs: - # Separate documented kwargs from other flags using a message - if flag_items: - message = 'The following flags are also accepted.' - item = _CreateItem(message, None, indent=4) - flag_items.append(item) - flag_items.extend(documented_kwargs) - description = _GetArgDescription(spec.varkw, docstring_info) - if documented_kwargs: - message = 'Additional undocumented flags may also be accepted.' - elif flag_items: - message = 'Additional flags are accepted.' - else: - message = 'Flags are accepted.' + message = ('Additional flags are accepted.' + if flag_items else + 'Flags are accepted.') item = _CreateItem(message, description, indent=4) flag_items.append(item) @@ -254,15 +230,14 @@ def _ArgsAndFlagsSections(info, spec, metadata): return args_and_flags_sections, notes_sections -def _UsageDetailsSections(component, actions_grouped_by_kind): +def _UsageDetailsSections(component, actions_grouped_by_kind,help_sequence=None): """The usage details sections of the help string.""" groups, commands, values, indexes = actions_grouped_by_kind - sections = [] if groups.members: sections.append(_MakeUsageDetailsSection(groups)) if commands.members: - sections.append(_MakeUsageDetailsSection(commands)) + sections.append(_MakeUsageDetailsSection(commands,help_sequence)) if values.members: sections.append(_ValuesUsageDetailsSection(component, values)) if indexes.members: @@ -388,143 +363,43 @@ def _CreateOutputSection(name, content): content=formatting.Indent(content, SECTION_INDENTATION)) -def _CreateArgItem(arg, docstring_info, spec): +def _CreateArgItem(arg, docstring_info): """Returns a string describing a positional argument. Args: arg: The name of the positional argument. 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. Returns: A string to be used in constructing the help screen for the function. """ - - # 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(arg, docstring_info) - arg_string = formatting.BoldUnderline(arg.upper()) - - arg_type = _GetArgType(arg, spec) - 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)) - - description = '\n'.join(part for part in (arg_type, description) if part) + arg = arg.upper() + return _CreateItem(formatting.BoldUnderline(arg), description, indent=4) - return _CreateItem(arg_string, description, indent=SUBSECTION_INDENTATION) - -def _CreateFlagItem(flag, docstring_info, spec, required=False, - flag_string=None): - """Returns a string describing a flag using docstring and FullArgSpec info. +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. - 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())) + 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_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 _GetArgType(arg, spec): - """Returns a string describing the type of an argument. - - Args: - arg: The name of the argument. - spec: An instance of fire.inspectutils.FullArgSpec, containing type and - default information about the arguments to a callable. - Returns: - A string to be used in constructing the help screen for the function, the - empty string if the argument type is not available. - """ - if arg in spec.annotations: - arg_type = spec.annotations[arg] - try: - if sys.version_info[0:2] >= (3, 3): - return arg_type.__qualname__ - return arg_type.__name__ - except AttributeError: - # Some typing objects, such as typing.Union do not have either a __name__ - # or __qualname__ attribute. - # repr(typing.Union[int, str]) will return ': typing.Union[int, str]' - return repr(arg_type) - return '' - - -def _GetArgDefault(flag, spec): - """Returns a string describing a flag's default value. - - Args: - flag: The name of the flag. - spec: An instance of fire.inspectutils.FullArgSpec, containing type and - default information about the arguments to a callable. - Returns: - A string to be used in constructing the help screen for the function, the - empty string if the flag does not have a default or the default is not - available. - """ - num_defaults = len(spec.defaults) - args_with_defaults = spec.args[-num_defaults:] - - for arg, default in zip(args_with_defaults, spec.defaults): - if arg == flag: - return repr(default) - return '' + flag += ' (required)' + return _CreateItem(flag, description, indent=4) def _CreateItem(name, description, indent=2): @@ -543,7 +418,7 @@ def _GetArgDescription(name, docstring_info): return None -def _MakeUsageDetailsSection(action_group): +def _MakeUsageDetailsSection(action_group, help_sequence): """Creates a usage details section for the provided action group.""" item_strings = [] for name, member in action_group.GetItems(): @@ -560,6 +435,13 @@ def _MakeUsageDetailsSection(action_group): summary = None item = _CreateItem(name, summary) item_strings.append(item) + + if help_sequence: + help_sequence = [x for x in help_sequence if x in item_strings] + item_strings = [x for x in item_strings if x not in help_sequence] + help_sequence.extend(item_strings) + item_strings = help_sequence + return (action_group.plural.upper(), _NewChoicesSection(action_group.name.upper(), item_strings)) From 28045382b8485c513def4757ffd15d599a8123bd Mon Sep 17 00:00:00 2001 From: varunjohn786 <61010611+varunjohn786@users.noreply.github.com> Date: Fri, 8 Jan 2021 17:50:53 +0100 Subject: [PATCH 3/9] resolved commit related issues --- fire/core.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fire/core.py b/fire/core.py index fc722056..d598bf42 100644 --- a/fire/core.py +++ b/fire/core.py @@ -74,6 +74,9 @@ def main(argv): from fire.console import console_io import six +if six.PY34: + import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error + def Fire(component=None, command=None, name=None, help_sequence=None): """This function, Fire, is the main entrypoint for Python Fire. @@ -669,7 +672,13 @@ def _CallAndUpdateTrace(component, args, component_trace, treatment='class', fn = component.__call__ if treatment == 'callable' else component parse = _MakeParseFn(fn, metadata) (varargs, kwargs), consumed_args, remaining_args, capacity = parse(args) - component = fn(*varargs, **kwargs) + + # Call the function. + if inspectutils.IsCoroutineFunction(fn): + loop = asyncio.get_event_loop() + component = loop.run_until_complete(fn(*varargs, **kwargs)) + else: + component = fn(*varargs, **kwargs) if treatment == 'class': action = trace.INSTANTIATED_CLASS From d13d8d43f69a704e4990e2e376b1af054a3ea3c2 Mon Sep 17 00:00:00 2001 From: varunjohn786 <61010611+varunjohn786@users.noreply.github.com> Date: Fri, 8 Jan 2021 17:51:35 +0100 Subject: [PATCH 4/9] Resolved issues in previous commit. --- fire/helptext.py | 169 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 146 insertions(+), 23 deletions(-) diff --git a/fire/helptext.py b/fire/helptext.py index ecfd486c..e6588b56 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -34,16 +34,19 @@ from __future__ import print_function import itertools +import sys from fire import completion from fire import custom_descriptions from fire import decorators +from fire import docstrings from fire import formatting from fire import inspectutils from fire import value_types LINE_LENGTH = 80 SECTION_INDENTATION = 4 +SUBSECTION_INDENTATION = 4 def HelpText(component, trace=None, verbose=False, help_sequence=None): @@ -79,6 +82,7 @@ def HelpText(component, trace=None, verbose=False, help_sequence=None): notes_sections = [] usage_details_sections = _UsageDetailsSections(component, actions_grouped_by_kind, help_sequence=help_sequence) + sections = ( [name_section, synopsis_section, description_section] + args_and_flags_sections @@ -170,7 +174,7 @@ def _DescriptionSection(component, info): def _CreateKeywordOnlyFlagItem(flag, docstring_info, spec): return _CreateFlagItem( - flag, docstring_info, required=flag not in spec.kwonlydefaults) + flag, docstring_info, spec, required=flag not in spec.kwonlydefaults) def _ArgsAndFlagsSections(info, spec, metadata): @@ -187,13 +191,13 @@ def _ArgsAndFlagsSections(info, spec, metadata): docstring_info = info['docstring_info'] arg_items = [ - _CreateArgItem(arg, docstring_info) + _CreateArgItem(arg, docstring_info, spec) for arg in args_with_no_defaults ] if spec.varargs: arg_items.append( - _CreateArgItem(spec.varargs, docstring_info) + _CreateArgItem(spec.varargs, docstring_info, spec) ) if arg_items: @@ -206,7 +210,7 @@ def _ArgsAndFlagsSections(info, spec, metadata): ) positional_flag_items = [ - _CreateFlagItem(flag, docstring_info, required=False) + _CreateFlagItem(flag, docstring_info, spec, required=False) for flag in args_with_defaults ] kwonly_flag_items = [ @@ -216,10 +220,30 @@ def _ArgsAndFlagsSections(info, spec, metadata): flag_items = positional_flag_items + kwonly_flag_items if spec.varkw: + # Include kwargs documented via :key param: + flag_string = '--{name}' + documented_kwargs = [] + for flag in docstring_info.args or []: + if isinstance(flag, docstrings.KwargInfo): + flag_item = _CreateFlagItem( + flag.name, docstring_info, spec, + flag_string=flag_string.format(name=flag.name)) + documented_kwargs.append(flag_item) + if documented_kwargs: + # Separate documented kwargs from other flags using a message + if flag_items: + message = 'The following flags are also accepted.' + item = _CreateItem(message, None, indent=4) + flag_items.append(item) + flag_items.extend(documented_kwargs) + description = _GetArgDescription(spec.varkw, docstring_info) - message = ('Additional flags are accepted.' - if flag_items else - 'Flags are accepted.') + if documented_kwargs: + message = 'Additional undocumented flags may also be accepted.' + elif flag_items: + message = 'Additional flags are accepted.' + else: + message = 'Flags are accepted.' item = _CreateItem(message, description, indent=4) flag_items.append(item) @@ -230,14 +254,15 @@ def _ArgsAndFlagsSections(info, spec, metadata): return args_and_flags_sections, notes_sections -def _UsageDetailsSections(component, actions_grouped_by_kind,help_sequence=None): +def _UsageDetailsSections(component, actions_grouped_by_kind, help_sequence=None): """The usage details sections of the help string.""" groups, commands, values, indexes = actions_grouped_by_kind + sections = [] if groups.members: sections.append(_MakeUsageDetailsSection(groups)) if commands.members: - sections.append(_MakeUsageDetailsSection(commands,help_sequence)) + sections.append(_MakeUsageDetailsSection(commands,help_sequence=help_sequence)) if values.members: sections.append(_ValuesUsageDetailsSection(component, values)) if indexes.members: @@ -363,43 +388,143 @@ def _CreateOutputSection(name, content): content=formatting.Indent(content, SECTION_INDENTATION)) -def _CreateArgItem(arg, docstring_info): +def _CreateArgItem(arg, docstring_info, spec): """Returns a string describing a positional argument. Args: arg: The name of the positional argument. 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. Returns: A string to be used in constructing the help screen for the function. """ + + # 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(arg, docstring_info) - arg = arg.upper() - return _CreateItem(formatting.BoldUnderline(arg), description, indent=4) + arg_string = formatting.BoldUnderline(arg.upper()) + + arg_type = _GetArgType(arg, spec) + 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)) + + description = '\n'.join(part for part in (arg_type, description) if part) + return _CreateItem(arg_string, description, indent=SUBSECTION_INDENTATION) -def _CreateFlagItem(flag, docstring_info, required=False): - """Returns a string describing a flag using information from the docstring. + +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) - flag_string_template = '--{flag_name}={flag_name_upper}' - flag = flag_string_template.format( - flag_name=flag, - flag_name_upper=formatting.Underline(flag.upper())) + 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 += ' (required)' - return _CreateItem(flag, description, indent=4) + 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 _GetArgType(arg, spec): + """Returns a string describing the type of an argument. + + Args: + arg: The name of the argument. + spec: An instance of fire.inspectutils.FullArgSpec, containing type and + default information about the arguments to a callable. + Returns: + A string to be used in constructing the help screen for the function, the + empty string if the argument type is not available. + """ + if arg in spec.annotations: + arg_type = spec.annotations[arg] + try: + if sys.version_info[0:2] >= (3, 3): + return arg_type.__qualname__ + return arg_type.__name__ + except AttributeError: + # Some typing objects, such as typing.Union do not have either a __name__ + # or __qualname__ attribute. + # repr(typing.Union[int, str]) will return ': typing.Union[int, str]' + return repr(arg_type) + return '' + + +def _GetArgDefault(flag, spec): + """Returns a string describing a flag's default value. + + Args: + flag: The name of the flag. + spec: An instance of fire.inspectutils.FullArgSpec, containing type and + default information about the arguments to a callable. + Returns: + A string to be used in constructing the help screen for the function, the + empty string if the flag does not have a default or the default is not + available. + """ + num_defaults = len(spec.defaults) + args_with_defaults = spec.args[-num_defaults:] + + for arg, default in zip(args_with_defaults, spec.defaults): + if arg == flag: + return repr(default) + return '' def _CreateItem(name, description, indent=2): @@ -418,7 +543,7 @@ def _GetArgDescription(name, docstring_info): return None -def _MakeUsageDetailsSection(action_group, help_sequence): +def _MakeUsageDetailsSection(action_group, help_sequence=None): """Creates a usage details section for the provided action group.""" item_strings = [] for name, member in action_group.GetItems(): @@ -435,13 +560,11 @@ def _MakeUsageDetailsSection(action_group, help_sequence): summary = None item = _CreateItem(name, summary) item_strings.append(item) - if help_sequence: help_sequence = [x for x in help_sequence if x in item_strings] item_strings = [x for x in item_strings if x not in help_sequence] help_sequence.extend(item_strings) item_strings = help_sequence - return (action_group.plural.upper(), _NewChoicesSection(action_group.name.upper(), item_strings)) From c947a83a399c446034437ffc52f6aac43c4b0837 Mon Sep 17 00:00:00 2001 From: varunjohn786 <61010611+varunjohn786@users.noreply.github.com> Date: Sat, 9 Jan 2021 16:27:10 +0100 Subject: [PATCH 5/9] Added help_sequence in _PrintResult function add help sequence functionality in _PrintResult function. --- fire/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fire/core.py b/fire/core.py index d598bf42..8f7195e0 100644 --- a/fire/core.py +++ b/fire/core.py @@ -147,7 +147,7 @@ def Fire(component=None, command=None, name=None, help_sequence=None): output = ['Fire trace:\n{trace}\n'.format(trace=component_trace)] result = component_trace.GetResult() help_text = helptext.HelpText( - result, trace=component_trace, verbose=component_trace.verbose) + result, trace=component_trace, verbose=component_trace.verbose, help_sequence=help_sequence) output.append(help_text) Display(output, out=sys.stderr) raise FireExit(0, component_trace) @@ -164,7 +164,7 @@ def Fire(component=None, command=None, name=None, help_sequence=None): raise FireExit(0, component_trace) # The command succeeded normally; print the result. - _PrintResult(component_trace, verbose=component_trace.verbose) + _PrintResult(component_trace, verbose=component_trace.verbose, help_sequence=help_sequence) result = component_trace.GetResult() return result @@ -241,7 +241,7 @@ def _IsHelpShortcut(component_trace, remaining_args): return show_help -def _PrintResult(component_trace, verbose=False): +def _PrintResult(component_trace, verbose=False, help_sequence=None): """Prints the result of the Fire call to stdout in a human readable way.""" # TODO(dbieber): Design human readable deserializable serialization method # and move serialization to its own module. @@ -267,7 +267,7 @@ def _PrintResult(component_trace, verbose=False): print(result) else: help_text = helptext.HelpText( - result, trace=component_trace, verbose=verbose) + result, trace=component_trace, verbose=verbose, help_sequence=help_sequence) output = [help_text] Display(output, out=sys.stdout) From dc8d723d6ac0aee8df04a58e2e6b36ec8c72278f Mon Sep 17 00:00:00 2001 From: varunjohn786 <61010611+varunjohn786@users.noreply.github.com> Date: Sun, 10 Jan 2021 00:27:39 +0100 Subject: [PATCH 6/9] Updated helptest.py file Updated logic of the code so that description of command doesn't affect the order sequence --- fire/helptext.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fire/helptext.py b/fire/helptext.py index e6588b56..3b481637 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -560,11 +560,15 @@ def _MakeUsageDetailsSection(action_group, help_sequence=None): summary = None item = _CreateItem(name, summary) item_strings.append(item) + + if help_sequence: - help_sequence = [x for x in help_sequence if x in item_strings] - item_strings = [x for x in item_strings if x not in help_sequence] - help_sequence.extend(item_strings) - item_strings = help_sequence + com_names = [name for name, memeber in action_group.GetItems()] + help_sequence = [x for x in help_sequence if x in com_names] + com_names_reorder = [x for x in com_names if x not in help_sequence] + help_sequence.extend(com_names_reorder) + com_names_reorder = help_sequence + item_strings = [item_strings[i] for x in com_names_reorder for i in range(len(com_names)) if x == com_names[i]] return (action_group.plural.upper(), _NewChoicesSection(action_group.name.upper(), item_strings)) From 3c74e68ae0ef75f9c0f4490c1beaa014bf0f2bd2 Mon Sep 17 00:00:00 2001 From: varunjohn786 <61010611+varunjohn786@users.noreply.github.com> Date: Sun, 10 Jan 2021 00:35:59 +0100 Subject: [PATCH 7/9] Updated core.py file Updated core.py file, so that _DisplayError function also shows reordered commands. --- fire/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fire/core.py b/fire/core.py index 8f7195e0..049c6c2c 100644 --- a/fire/core.py +++ b/fire/core.py @@ -141,7 +141,7 @@ def Fire(component=None, command=None, name=None, help_sequence=None): component_trace = _Fire(component, args, parsed_flag_args, context, name) if component_trace.HasError(): - _DisplayError(component_trace) + _DisplayError(component_trace, help_sequence=help_sequence) raise FireExit(2, component_trace) if component_trace.show_trace and component_trace.show_help: output = ['Fire trace:\n{trace}\n'.format(trace=component_trace)] @@ -272,7 +272,7 @@ def _PrintResult(component_trace, verbose=False, help_sequence=None): Display(output, out=sys.stdout) -def _DisplayError(component_trace): +def _DisplayError(component_trace, help_sequence=None): """Prints the Fire trace and the error to stdout.""" result = component_trace.GetResult() @@ -287,7 +287,7 @@ def _DisplayError(component_trace): print('INFO: Showing help with the command {cmd}.\n'.format( cmd=pipes.quote(command)), file=sys.stderr) help_text = helptext.HelpText(result, trace=component_trace, - verbose=component_trace.verbose) + verbose=component_trace.verbose, help_sequence=help_sequence) output.append(help_text) Display(output, out=sys.stderr) else: @@ -295,7 +295,7 @@ def _DisplayError(component_trace): + component_trace.elements[-1].ErrorAsStr(), file=sys.stderr) error_text = helptext.UsageText(result, trace=component_trace, - verbose=component_trace.verbose) + verbose=component_trace.verbose, help_sequence=help_sequence) print(error_text, file=sys.stderr) From 8d03a1e3a18764d49efbb8846f5d6b027664f0e8 Mon Sep 17 00:00:00 2001 From: shubham Date: Thu, 21 Jan 2021 01:29:20 +0100 Subject: [PATCH 8/9] Updated the following: 1. Added Docstrings 2. Test Case 3. Test Components --- fire/core.py | 5 +++++ fire/helptext.py | 4 ++++ fire/helptext_test.py | 33 +++++++++++++++++++++++++++++++++ fire/test_components.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/fire/core.py b/fire/core.py index 049c6c2c..c6b02b3f 100644 --- a/fire/core.py +++ b/fire/core.py @@ -94,6 +94,11 @@ def Fire(component=None, command=None, name=None, help_sequence=None): a string or a list of strings; a list of strings is preferred. name: Optional. The name of the command as entered at the command line. Used in interactive mode and for generating the completion script. + help_sequence: Optional. If supplied, the sequence of commands + will be reordered based on provided list in argument. They will + be displayed before all the other commands. This should be + a list of strings. + Returns: The result of executing the Fire command. Execution begins with the initial target component. The component is updated by using the command arguments diff --git a/fire/helptext.py b/fire/helptext.py index 3b481637..def4c869 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -57,6 +57,10 @@ def HelpText(component, trace=None, verbose=False, help_sequence=None): 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. + help_sequence: Optional. If supplied, the sequence of commands + will be reordered based on provided list in argument. They will + be displayed before all the other commands. This should be + a list of strings. Returns: The full help screen as a string. diff --git a/fire/helptext_test.py b/fire/helptext_test.py index 3cb40fbe..7513c876 100644 --- a/fire/helptext_test.py +++ b/fire/helptext_test.py @@ -423,6 +423,39 @@ def testHelpTextNameSectionCommandWithSeparatorVerbose(self): self.assertIn('double -', help_screen) self.assertIn('double - -', help_screen) + def testHelpTextWithHelpSequence(self): + component = tc.ClassWithMultipleCommands() + help_output = helptext.HelpText(component=component, help_sequence=['print_msg']) + expected_output = """ + NAME + - Test class for testing help text output with help_sequence=True argument. + +SYNOPSIS + COMMAND | VALUE + +DESCRIPTION + This is some detail description of this test class. + +COMMANDS + COMMAND is one of the following: + + print_msg + Prints a message. + + append_msg + Appends string to current message. + + update_msg + Adds a message. + +VALUES + VALUE is one of the following: + + message + The default message to print.""" + self.assertEqual(textwrap.dedent(expected_output).strip(), + help_output.strip()) + class UsageTest(testutils.BaseTestCase): diff --git a/fire/test_components.py b/fire/test_components.py index eee9a07c..9bf868b2 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -370,6 +370,39 @@ def print_msg(self, msg=None): print(msg) +class ClassWithMultipleCommands(object): + """Test class for testing help text output with help_sequence=True argument. + + This is some detail description of this test class. + """ + + def __init__(self, message='Hello!'): + """Constructor of the test class. + + Constructs a new ClassWithDocstring object. + + Args: + message: The default message to print. + """ + self.message = message + + def print_msg(self, msg=None): + """Prints a message.""" + if msg is None: + msg = self.message + print(msg) + + def update_msg(self, msg=None): + """Adds a message.""" + self.message = msg + print(msg) + + def append_msg(self, msg=None): + """Appends string to current message.""" + self.message = self.message + ' ' + msg + print(msg) + + class ClassWithMultilineDocstring(object): """Test class for testing help text output with multiline docstring. From b8e6052b68f82d33d448b4cf635ed6d73d2fecef Mon Sep 17 00:00:00 2001 From: Varun John <61010611+varunjohn786@users.noreply.github.com> Date: Sun, 28 Feb 2021 23:23:32 +0530 Subject: [PATCH 9/9] Update fire/core.py Updated core.py file to correct mistake in UsageText() function call. --- fire/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fire/core.py b/fire/core.py index c6b02b3f..55a5ae86 100644 --- a/fire/core.py +++ b/fire/core.py @@ -300,7 +300,7 @@ def _DisplayError(component_trace, help_sequence=None): + component_trace.elements[-1].ErrorAsStr(), file=sys.stderr) error_text = helptext.UsageText(result, trace=component_trace, - verbose=component_trace.verbose, help_sequence=help_sequence) + verbose=component_trace.verbose) print(error_text, file=sys.stderr)