Beispiel #1
0
 def testCreateParser(self):
     self.assertIsNotNone(parser.CreateParser())
Beispiel #2
0
def _Fire(component, args, context, name=None):
    """Execute a Fire command on a target component using the args supplied.

  Arguments that come after a final isolated '--' are treated as Flags, eg for
  interactive mode or completion script generation.

  Other arguments are consumed by the execution of the Fire command, eg in the
  traversal of the members of the component, or in calling a function or
  instantiating a class found during the traversal.

  The steps performed by this method are:

  1. Parse any Flag args (the args after the final --)

  2. Start with component as the current component.
  2a. If the current component is a class, instantiate it using args from args.
  2b. If the current component is a routine, call it using args from args.
  2c. Otherwise access a member from component using an arg from args.
  2d. Repeat 2a-2c until no args remain.

  3a. Embed into ipython REPL if interactive mode is selected.
  3b. Generate a completion script if that flag is provided.

  In step 2, arguments will only ever be consumed up to a separator; a single
  step will never consume arguments from both sides of a separator.
  The separator defaults to a hyphen (-), and can be overwritten with the
  --separator Fire argument.

  Args:
    component: The target component for Fire.
    args: A list of args to consume in Firing on the component, usually from
        the command line.
    context: A dict with the local and global variables available at the call
        to Fire.
    name: Optional. The name of the command. Used in interactive mode and in
        the tab completion script.
  Returns:
    FireTrace of components starting with component, tracing Fire's execution
        path as it consumes args.
  Raises:
    ValueError: If there are arguments that cannot be consumed.
    ValueError: If --completion is specified but no name available.
  """
    args, flag_args = parser.SeparateFlagArgs(args)

    argparser = parser.CreateParser()
    parsed_flag_args, unused_args = argparser.parse_known_args(flag_args)
    verbose = parsed_flag_args.verbose
    interactive = parsed_flag_args.interactive
    separator = parsed_flag_args.separator
    show_completion = parsed_flag_args.completion
    show_help = parsed_flag_args.help
    show_trace = parsed_flag_args.trace

    # component can be a module, class, routine, object, etc.
    if component is None:
        component = context

    initial_component = component
    component_trace = trace.FireTrace(initial_component=initial_component,
                                      name=name,
                                      separator=separator,
                                      verbose=verbose,
                                      show_help=show_help,
                                      show_trace=show_trace)

    instance = None
    remaining_args = args
    while True:
        last_component = component
        initial_args = remaining_args

        if not remaining_args and (show_help or interactive or show_trace
                                   or show_completion):
            # Don't initialize the final class or call the final function unless
            # there's a separator after it, and instead process the current component.
            break

        saved_args = []
        used_separator = False
        if separator in remaining_args:
            # For the current component, only use arguments up to the separator.
            separator_index = remaining_args.index(separator)
            saved_args = remaining_args[separator_index + 1:]
            remaining_args = remaining_args[:separator_index]
            used_separator = True
        assert separator not in remaining_args

        if inspect.isclass(component) or inspect.isroutine(component):
            # The component is a class or a routine; we'll try to initialize it or
            # call it.
            isclass = inspect.isclass(component)

            try:
                target = component.__name__
                filename, lineno = _GetFileAndLine(component)

                component, consumed_args, remaining_args, capacity = _CallCallable(
                    component, remaining_args)

                # Update the trace.
                if isclass:
                    component_trace.AddInstantiatedClass(
                        component, target, consumed_args, filename, lineno,
                        capacity)
                else:
                    component_trace.AddCalledRoutine(component, target,
                                                     consumed_args, filename,
                                                     lineno, capacity)

            except FireError as error:
                component_trace.AddError(error, initial_args)
                return component_trace

            if last_component is initial_component:
                # If the initial component is a class, keep an instance for use with -i.
                instance = component

        elif isinstance(component, (list, tuple)) and remaining_args:
            # The component is a tuple or list; we'll try to access a member.
            arg = remaining_args[0]
            try:
                index = int(arg)
                component = component[index]
            except (ValueError, IndexError):
                error = FireError(
                    'Unable to index into component with argument:', arg)
                component_trace.AddError(error, initial_args)
                return component_trace

            remaining_args = remaining_args[1:]
            filename = None
            lineno = None
            component_trace.AddAccessedProperty(component, index, [arg],
                                                filename, lineno)

        elif isinstance(component, dict) and remaining_args:
            # The component is a dict; we'll try to access a member.
            target = remaining_args[0]
            if target in component:
                component = component[target]
            elif target.replace('-', '_') in component:
                component = component[target.replace('-', '_')]
            else:
                # The target isn't present in the dict as a string, but maybe it is as
                # another type.
                # TODO: Consider alternatives for accessing non-string keys.
                found_target = False
                for key, value in component.items():
                    if target == str(key):
                        component = value
                        found_target = True
                        break
                if not found_target:
                    error = FireError('Cannot find target in dict:', target,
                                      component)
                    component_trace.AddError(error, initial_args)
                    return component_trace

            remaining_args = remaining_args[1:]
            filename = None
            lineno = None
            component_trace.AddAccessedProperty(component, target, [target],
                                                filename, lineno)

        elif remaining_args:
            # We'll try to access a member of the component.
            try:
                target = remaining_args[0]

                component, consumed_args, remaining_args = _GetMember(
                    component, remaining_args)

                filename, lineno = _GetFileAndLine(component)

                component_trace.AddAccessedProperty(component, target,
                                                    consumed_args, filename,
                                                    lineno)

            except FireError as error:
                component_trace.AddError(error, initial_args)
                return component_trace

        if used_separator:
            # Add back in the arguments from after the separator.
            if remaining_args:
                remaining_args = remaining_args + [separator] + saved_args
            elif (inspect.isclass(last_component)
                  or inspect.isroutine(last_component)):
                remaining_args = saved_args
                component_trace.AddSeparator()
            elif component is not last_component:
                remaining_args = [separator] + saved_args
            else:
                # It was an unnecessary separator.
                remaining_args = saved_args

        if component is last_component and remaining_args == initial_args:
            # We're making no progress.
            break

    if remaining_args:
        component_trace.AddError(
            FireError('Could not consume arguments:', remaining_args),
            initial_args)
        return component_trace

    if show_completion:
        if name is None:
            raise ValueError(
                'Cannot make completion script without command name')
        script = CompletionScript(name, initial_component)
        component_trace.AddCompletionScript(script)

    if interactive:
        variables = context.copy()

        if name is not None:
            variables[name] = initial_component
        variables['component'] = initial_component
        variables['result'] = component
        variables['trace'] = component_trace

        if instance is not None:
            variables['self'] = instance

        interact.Embed(variables, verbose)

        component_trace.AddInteractiveMode()

    return component_trace
Beispiel #3
0
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_string = helptext.HelpString(result, component_trace,
                                          component_trace.verbose)
        output.append(help_string)
        Display(output)
        raise FireExit(0, component_trace)
    if component_trace.show_trace:
        output = ['Fire trace:\n{trace}'.format(trace=component_trace)]
        Display(output)
        raise FireExit(0, component_trace)
    if component_trace.show_help:
        result = component_trace.GetResult()
        help_string = helptext.HelpString(result, component_trace,
                                          component_trace.verbose)
        output = [help_string]
        Display(output)
        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