コード例 #1
0
ファイル: core.py プロジェクト: rameshgodishela/python-fire
def _MakeParseFn(fn):
    """Creates a parse function for fn.

  Args:
    fn: The function or class to create the parse function for.
  Returns:
    A parse function for fn. The parse function accepts a list of arguments
    and returns (varargs, kwargs), remaining_args. The original function fn
    can then be called with fn(*varargs, **kwargs). The remaining_args are
    the leftover args from the arguments to the parse function.
  """
    fn_spec = inspectutils.GetFullArgSpec(fn)
    metadata = decorators.GetMetadata(fn)

    # Note: num_required_args is the number of positional arguments without
    # default values. All of these arguments are required.
    num_required_args = len(fn_spec.args) - len(fn_spec.defaults)
    required_kwonly = set(fn_spec.kwonlyargs) - set(fn_spec.kwonlydefaults)

    def _ParseFn(args):
        """Parses the list of `args` into (varargs, kwargs), remaining_args."""
        kwargs, remaining_kwargs, remaining_args = _ParseKeywordArgs(
            args, fn_spec)

        # Note: _ParseArgs modifies kwargs.
        parsed_args, kwargs, remaining_args, capacity = _ParseArgs(
            fn_spec.args, fn_spec.defaults, num_required_args, kwargs,
            remaining_args, metadata)

        if fn_spec.varargs or fn_spec.varkw:
            # If we're allowed *varargs or **kwargs, there's always capacity.
            capacity = True

        extra_kw = set(kwargs) - set(fn_spec.kwonlyargs)
        if fn_spec.varkw is None and extra_kw:
            raise FireError('Unexpected kwargs present:', extra_kw)

        missing_kwonly = set(required_kwonly) - set(kwargs)
        if missing_kwonly:
            raise FireError('Missing required flags:', missing_kwonly)

        # If we accept *varargs, then use all remaining arguments for *varargs.
        if fn_spec.varargs is not None:
            varargs, remaining_args = remaining_args, []
        else:
            varargs = []

        for index, value in enumerate(varargs):
            varargs[index] = _ParseValue(value, None, None, metadata)

        varargs = parsed_args + varargs
        remaining_args += remaining_kwargs

        consumed_args = args[:len(args) - len(remaining_args)]
        return (varargs, kwargs), consumed_args, remaining_args, capacity

    return _ParseFn
コード例 #2
0
ファイル: core.py プロジェクト: iln-sk/python-fire
def _CallAndUpdateTrace(component,
                        args,
                        component_trace,
                        treatment='class',
                        target=None):
    """Call the component by consuming args from args, and update the FireTrace.

  The component could be a class, a routine, or a callable object. This function
  calls the component and adds the appropriate action to component_trace.

  Args:
    component: The component to call
    args: Args for calling the component
    component_trace: FireTrace object that contains action trace
    treatment: Type of treatment used. Indicating whether we treat the component
        as a class, a routine, or a callable.
    target: Target in FireTrace element, default is None. If the value is None,
        the component itself will be used as target.
  Returns:
    component: The object that is the result of the callable call.
    remaining_args: The remaining args that haven't been consumed yet.
  """
    if not target:
        target = component
    filename, lineno = inspectutils.GetFileAndLine(component)
    metadata = decorators.GetMetadata(component)
    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)

    if treatment == 'class':
        action = trace.INSTANTIATED_CLASS
    elif treatment == 'routine':
        action = trace.CALLED_ROUTINE
    else:
        action = trace.CALLED_CALLABLE
    component_trace.AddCalledComponent(component,
                                       target,
                                       consumed_args,
                                       filename,
                                       lineno,
                                       capacity,
                                       action=action)

    return component, remaining_args
コード例 #3
0
def HelpText(component, trace=None, verbose=False):
  """Gets the help string for the current component, suitalbe 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 inspect.isroutine(component) or inspect.isclass(component):
    # For functions (ARGUMENTS / POSITIONAL ARGUMENTS, FLAGS)
    args_and_flags_sections, notes_sections = _ArgsAndFlagsSections(
        info, spec, metadata)
    usage_details_sections = []
  else:
    # For objects (GROUPS, COMMANDS, VALUES, INDEXES)
    # TODO(dbieber): Show callable function usage in help text.
    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
  )
コード例 #4
0
def UsageTextForFunction(component, trace=None, verbose=False):
  """Returns usage text for function objects.

  Args:
    component: The component to determine the usage text for.
    trace: The Fire trace object containing all metadata of current execution.
    verbose: Whether to display the usage text in verbose mode.

  Returns:
    String suitable for display in an error screen.
  """
  del verbose  # Unused.

  output_template = """Usage: {current_command} {args_and_flags}
{availability_lines}
For detailed information on this command, run:
  {current_command}{hyphen_hyphen} --help"""

  if trace:
    command = trace.GetCommand()
    needs_separating_hyphen_hyphen = trace.NeedsSeparatingHyphenHyphen()
  else:
    command = None
    needs_separating_hyphen_hyphen = False

  if not command:
    command = ''

  spec = inspectutils.GetFullArgSpec(component)
  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, show flag syntax for args.
  metadata = decorators.GetMetadata(component)
  accepts_positional_args = metadata.get(decorators.ACCEPTS_POSITIONAL_ARGS)
  if not accepts_positional_args:
    items = ['--{arg}={upper}'.format(arg=arg, upper=arg.upper())
             for arg in args_with_no_defaults]
  else:
    items = [arg.upper() for arg in args_with_no_defaults]

  # If there are any arguments that are treated as flags:
  if args_with_defaults or spec.kwonlyargs or spec.varkw:
    items.append('<flags>')

  optional_flags = [('--' + flag) for flag in args_with_defaults]
  required_flags = [('--' + flag) for flag in spec.kwonlyargs]

  # Flags section:
  availability_lines = []
  if optional_flags:
    availability_lines.append(
        _CreateAvailabilityLine(header='Optional flags:', items=optional_flags,
                                header_indent=0))
  if required_flags:
    availability_lines.append(
        _CreateAvailabilityLine(header='Required flags:', items=required_flags,
                                header_indent=0))
  if spec.varkw:
    additional_flags = ('Additional flags are accepted.'
                        if optional_flags or required_flags else
                        'Flags are accepted.')
    availability_lines.append(additional_flags + '\n')

  if availability_lines:
    # Start the section with blank lines.
    availability_lines.insert(0, '\n')

  if spec.varargs:
    items.append('[{varargs}]...'.format(varargs=spec.varargs.upper()))

  args_and_flags = ' '.join(items)

  hyphen_hyphen = ' --' if needs_separating_hyphen_hyphen else ''

  return output_template.format(
      current_command=command,
      args_and_flags=args_and_flags,
      availability_lines=''.join(availability_lines),
      hyphen_hyphen=hyphen_hyphen)
コード例 #5
0
ファイル: helptext.py プロジェクト: loctv/python-fire
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)
コード例 #6
0
def UsageText(component, trace=None, verbose=False):
  """Returns usage text for the given component.

  Args:
    component: The component to determine the usage text for.
    trace: The Fire trace object containing all metadata of current execution.
    verbose: Whether to display the usage text in verbose mode.

  Returns:
    String suitable for display in an error screen.
  """
  output_template = """Usage: {continued_command}
{availability_lines}
For detailed information on this command, run:
  {help_command}"""

  # Get the command so far:
  if trace:
    command = trace.GetCommand()
    needs_separating_hyphen_hyphen = trace.NeedsSeparatingHyphenHyphen()
  else:
    command = None
    needs_separating_hyphen_hyphen = False

  if not command:
    command = ''

  # Build the continuations for the command:
  continued_command = command

  spec = inspectutils.GetFullArgSpec(component)
  metadata = decorators.GetMetadata(component)

  # Usage for objects.
  actions_grouped_by_kind = _GetActionsGroupedByKind(component, verbose=verbose)
  possible_actions = _GetPossibleActions(actions_grouped_by_kind)

  continuations = []
  if possible_actions:
    continuations.append(_GetPossibleActionsUsageString(possible_actions))

  availability_lines = _UsageAvailabilityLines(actions_grouped_by_kind)

  if callable(component):
    callable_items = _GetCallableUsageItems(spec, metadata)
    if callable_items:
      continuations.append(' '.join(callable_items))
    elif trace:
      continuations.append(trace.separator)
    availability_lines.extend(_GetCallableAvailabilityLines(spec))

  if continuations:
    continued_command += ' ' + ' | '.join(continuations)
  help_command = (
      command
      + (' -- ' if needs_separating_hyphen_hyphen else ' ')
      + '--help'
  )

  return output_template.format(
      continued_command=continued_command,
      availability_lines=''.join(availability_lines),
      help_command=help_command)