def testHelpTextFunctionWithKwargsAndDefaults(self): component = tc.fn_with_kwarg_and_defaults help_screen = helptext.HelpText(component=component, trace=trace.FireTrace(component, name='text')) self.assertIn('NAME\n text', help_screen) self.assertIn('SYNOPSIS\n text ARG1 ARG2 <flags>', help_screen) self.assertIn('DESCRIPTION\n Function with kwarg', help_screen) self.assertIn( 'FLAGS\n --opt=OPT\n Default: True\n' ' The following flags are also accepted.' '\n --arg3\n Description of arg3.\n ' 'Additional undocumented flags may also be accepted.', help_screen)
def testHelpTextNoDefaultsObject(self): component = tc.NoDefaults() help_screen = helptext.HelpText( component=component, 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 following:', help_screen) self.assertIn('double', help_screen) self.assertIn('triple', help_screen) self.assertNotIn('NOTES', help_screen)
def testHelpTextFunctionWithTypesAndDefaultNone(self): component = tc.py3p5.WithDefaultsAndTypes().get_int help_screen = helptext.HelpText( component=component, trace=trace.FireTrace(component, name='get_int')) self.assertIn('NAME\n get_int', help_screen) self.assertIn('SYNOPSIS\n get_int <flags>', help_screen) self.assertNotIn('DESCRIPTION', help_screen) self.assertIn( 'FLAGS\n --value=VALUE\n' ' Type: Optional[int]\n Default: None', help_screen) self.assertNotIn('NOTES', help_screen)
def testHelpTextInt(self): component = 7 help_screen = helptext.HelpText(component=component, trace=trace.FireTrace(component, '7')) self.assertIn('NAME\n 7', help_screen) self.assertIn('SYNOPSIS\n 7 COMMAND | VALUE', help_screen) # TODO(zuhaochen): Change assertion after implementing custom # description for int. self.assertNotIn('DESCRIPTION', help_screen) self.assertIn('COMMANDS\n COMMAND is one of the following:\n', help_screen) self.assertIn('VALUES\n VALUE is one of the following:\n', help_screen)
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 testHelpTextObjectWithGroupAndValues(self): component = tc.TypedProperties() t = trace.FireTrace(component, name='TypedProperties') help_screen = helptext.HelpText( component=component, 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 testHelpTextFunctionWithTypes(self): component = tc.py3.WithTypes().double # pytype: disable=module-attr help_screen = helptext.HelpText( component=component, trace=trace.FireTrace(component, name='double')) self.assertIn('NAME\n double', help_screen) self.assertIn('SYNOPSIS\n double COUNT', help_screen) self.assertIn('DESCRIPTION', help_screen) self.assertIn( 'POSITIONAL ARGUMENTS\n COUNT\n Type: float', help_screen) self.assertIn( 'NOTES\n You can also use flags syntax for POSITIONAL ARGUMENTS', help_screen)
def testHelpTextEmptyList(self): component = [] help_screen = helptext.HelpText(component=component, 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 either since the list API could # potentially change between Python versions. self.assertIn('COMMANDS\n COMMAND is one of the following:\n', help_screen)
def testHelpTextFunctionWithLongDefaults(self): component = tc.WithDefaults().text help_screen = helptext.HelpText( component=component, trace=trace.FireTrace(component, name='text')) self.assertIn('NAME\n text', help_screen) self.assertIn('SYNOPSIS\n text <flags>', help_screen) self.assertNotIn('DESCRIPTION', help_screen) self.assertIn( 'FLAGS\n --string=STRING\n' ' Default: \'0001020304050607080910' '1112131415161718192021222324252627282...', help_screen) self.assertNotIn('NOTES', help_screen)
def testHelpTextFunction(self): component = tc.NoDefaults().double info = inspectutils.Info(component) help_screen = helptext.HelpText(component=component, info=info, trace=trace.FireTrace(component, name='double')) self.assertIn('NAME\n double', help_screen) self.assertIn('SYNOPSIS\n double COUNT', help_screen) self.assertNotIn('DESCRIPTION', help_screen) self.assertIn('POSITIONAL ARGUMENTS\n COUNT', help_screen) self.assertIn( 'NOTES\n You could also use flags syntax for POSITIONAL ARGUMENTS', help_screen)
def testHelpTextEmptyList(self): component = [] help_screen = helptext.HelpText(component=component, trace=trace.FireTrace( component, 'list')) self.assertIn('NAME\n list', help_screen) self.assertIn('SYNOPSIS\n list COMMAND', help_screen) # TODO(zuhaochen): Change assertion after custom description is # implemented for list type. self.assertNotIn('DESCRIPTION', help_screen) # We don't check the listed commands either since the list API could # potentially change between Python versions. self.assertIn('COMMANDS\n COMMAND is one of the following:\n', help_screen)
def testHelpTextFunctionWithTypesAndDefaultNone(self): component = tc.py3.WithDefaultsAndTypes().get_int # pytype: disable=module-attr help_screen = helptext.HelpText( component=component, trace=trace.FireTrace(component, name="get_int") ) self.assertIn("NAME\n get_int", help_screen) self.assertIn("SYNOPSIS\n get_int <flags>", help_screen) self.assertNotIn("DESCRIPTION", help_screen) self.assertIn( "FLAGS\n --value=VALUE\n" " Type: Optional[int]\n Default: None", help_screen, ) self.assertNotIn("NOTES", help_screen)
def testHelpTextFunctionWithLongDefaults(self): component = tc.WithDefaults().text help_screen = helptext.HelpText( component=component, trace=trace.FireTrace(component, name="text") ) self.assertIn("NAME\n text", help_screen) self.assertIn("SYNOPSIS\n text <flags>", help_screen) self.assertNotIn("DESCRIPTION", help_screen) self.assertIn( "FLAGS\n --string=STRING\n" " Default: '0001020304050607080910" "1112131415161718192021222324252627282...", help_screen, ) self.assertNotIn("NOTES", help_screen)
def testHelpTextFunctionWithLongTypes(self): component = tc.py3p5.WithTypes().long_type help_screen = helptext.HelpText( component=component, trace=trace.FireTrace(component, name='long_type')) self.assertIn('NAME\n long_type', help_screen) self.assertIn('SYNOPSIS\n long_type LONG_OBJ', help_screen) self.assertNotIn('DESCRIPTION', help_screen) self.assertIn( 'POSITIONAL ARGUMENTS\n LONG_OBJ\n' ' Type: typing.Tuple[typing.Tuple[' 'typing.Tuple[typing.Tuple[typing.Tupl...', help_screen) self.assertIn( 'NOTES\n You can also use flags syntax for POSITIONAL ARGUMENTS', help_screen)
def testHelpTextShortList(self): component = [10] help_screen = helptext.HelpText(component=component, trace=trace.FireTrace( component, 'list')) self.assertIn('NAME\n list', help_screen) self.assertIn('SYNOPSIS\n list COMMAND', help_screen) # The list docstring is messy, so it is not shown. self.assertNotIn('DESCRIPTION', 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 following:\n', help_screen) self.assertIn(' append\n', help_screen)
def testHelpTextShortList(self): component = [10] help_screen = helptext.HelpText( component=component, trace=trace.FireTrace(component, "list") ) self.assertIn("NAME\n list", help_screen) self.assertIn("SYNOPSIS\n list COMMAND", help_screen) # TODO(zuhaochen): Change assertion after custom description is # implemented for list type. self.assertNotIn("DESCRIPTION", 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 following:\n", help_screen) self.assertIn(" append\n", help_screen)
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 testHelpTextFunctionWithLongTypes(self): component = tc.py3.WithTypes().long_type help_screen = helptext.HelpText(component=component, trace=trace.FireTrace( component, name='long_type')) self.assertIn('NAME\n long_type', help_screen) self.assertIn('SYNOPSIS\n long_type LONG_OBJ', help_screen) self.assertNotIn('DESCRIPTION', help_screen) # TODO(dbieber): Assert type is displayed correctly. Type displays # differently in Travis vs in Google. # self.assertIn( # 'POSITIONAL ARGUMENTS\n LONG_OBJ\n' # ' Type: typing.Tuple[typing.Tuple[' # 'typing.Tuple[typing.Tuple[typing.Tupl...', # help_screen) self.assertIn( 'NOTES\n You can also use flags syntax for POSITIONAL ARGUMENTS', help_screen)
def testHelpScreenForFunctionFunctionWithDefaultArgs(self): component = tc.WithDefaults().double t = trace.FireTrace(component, name='double') help_output = helptext.HelpText(component, 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 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 _PrintResult(component_trace, verbose=False): """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. result = component_trace.GetResult() if isinstance(result, (list, set, types.GeneratorType)): for i in result: print(_OneLineResult(i)) elif inspect.isgeneratorfunction(result): raise NotImplementedError elif isinstance(result, dict): print(_DictAsString(result, verbose)) elif isinstance(result, tuple): print(_OneLineResult(result)) elif isinstance(result, value_types.VALUE_TYPES): print(result) elif result is not None: help_text = helptext.HelpText(result, component_trace, verbose) output = [help_text] Display(output, out=sys.stdout)
def testHelpScreenForFunctionDocstringWithLineBreak(self): component = tc.ClassWithMultilineDocstring.example_generator t = trace.FireTrace(component, name="example_generator") help_output = helptext.HelpText(component, 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 can also use flags syntax for POSITIONAL ARGUMENTS""" self.assertEqual(textwrap.dedent(expected_output).strip(), help_output.strip())
def _PrintResult(component_trace, verbose=False, serialize=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. result = component_trace.GetResult() # Allow users to modify the return value of the component and provide # custom formatting. if serialize: if not callable(serialize): raise FireError( 'The argument `serialize` must be empty or callable:', serialize) result = serialize(result) if value_types.HasCustomStr(result): # If the object has a custom __str__ method, rather than one inherited from # object, then we use that to serialize the object. print(str(result)) return if isinstance(result, (list, set, frozenset, types.GeneratorType)): for i in result: print(_OneLineResult(i)) elif inspect.isgeneratorfunction(result): raise NotImplementedError elif isinstance(result, dict) and value_types.IsSimpleGroup(result): print(_DictAsString(result, verbose)) elif isinstance(result, tuple): print(_OneLineResult(result)) elif isinstance(result, value_types.VALUE_TYPES): if result is not None: print(result) else: help_text = helptext.HelpText( result, trace=component_trace, verbose=verbose) output = [help_text] Display(output, out=sys.stdout)
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 Fire(component=None, command=None, name=None): """This function, Fire, is the main entrypoint for Python Fire. Executes a command either from the `command` argument or from sys.argv by recursively traversing the target object `component`'s members consuming arguments, evaluating functions, and instantiating classes as it goes. When building a CLI with Fire, your main method should call this function. Args: component: The initial target component. command: Optional. If supplied, this is the command executed. If not supplied, then the command is taken from sys.argv instead. This can be 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. Returns: The result of executing the Fire command. Execution begins with the initial target component. The component is updated by using the command arguments to either access a member of the current component, call the current component (if it's a function), or instantiate the current component (if it's a class). When all arguments are consumed and there's no function left to call or class left to instantiate, the resulting current component is the final result. Raises: ValueError: If the command argument is supplied, but not a string or a sequence of arguments. FireExit: When Fire encounters a FireError, Fire will raise a FireExit with code 2. When used with the help or trace flags, Fire will raise a FireExit with code 0 if successful. """ name = name or os.path.basename(sys.argv[0]) # Get args as a list. if isinstance(command, six.string_types): args = shlex.split(command) elif isinstance(command, (list, tuple)): args = command elif command is None: # Use the command line args by default if no command is specified. args = sys.argv[1:] else: raise ValueError( 'The command argument must be a string or a sequence of ' 'arguments.') args, flag_args = parser.SeparateFlagArgs(args) argparser = parser.CreateParser() parsed_flag_args, unused_args = argparser.parse_known_args(flag_args) context = {} if parsed_flag_args.interactive or component is None: # Determine the calling context. caller = inspect.stack()[1] caller_frame = caller[0] caller_globals = caller_frame.f_globals caller_locals = caller_frame.f_locals context.update(caller_globals) context.update(caller_locals) component_trace = _Fire(component, args, parsed_flag_args, context, name) if component_trace.HasError(): _DisplayError(component_trace) 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)] result = component_trace.GetResult() help_text = helptext.HelpText(result, trace=component_trace, verbose=component_trace.verbose) output.append(help_text) Display(output, out=sys.stderr) raise FireExit(0, component_trace) if component_trace.show_trace: output = ['Fire trace:\n{trace}'.format(trace=component_trace)] Display(output, out=sys.stderr) raise FireExit(0, component_trace) if component_trace.show_help: result = component_trace.GetResult() help_text = helptext.HelpText(result, trace=component_trace, verbose=component_trace.verbose) output = [help_text] Display(output, out=sys.stderr) raise FireExit(0, component_trace) # The command succeeded normally; print the result. _PrintResult(component_trace, verbose=component_trace.verbose) result = component_trace.GetResult() return result
def testHelpTextKeywordOnlyArgumentsWithoutDefault(self): component = tc.py3.KeywordOnly.double # pytype: disable=module-attr output = helptext.HelpText(component=component, trace=trace.FireTrace(component, 'double')) self.assertIn('NAME\n double', output) self.assertIn('FLAGS\n --count=COUNT (required)', output)
def testHelpTextKeywordOnlyArgumentsWithDefault(self): component = tc.py3.KeywordOnly.with_default # pytype: disable=module-attr output = helptext.HelpText( component=component, trace=trace.FireTrace(component, 'with_default')) self.assertIn('NAME\n with_default', output) self.assertIn('FLAGS\n --x=X', output)