Repository: google/python-fire
File: fire/inspectutils.py (Python)

Code before:
    fn: The function to analyze.
  Returns:
    An object containing the following fields:
      args: A list of the argument names accepted by the function.
      varargs: The name of the *varargs argument or None if there isn't one.
      varkw: The name of the **kwargs argument or None if there isn't one.
      defaults: A tuple of the defaults for the arguments that accept defaults.
      kwonlyargs: A list of argument names that must be passed with a keyword.
      kwonlydefaults: A dictionary of keyword only arguments and their defaults.
      annotations: A dictionary of arguments and their annotated types.
  """
  fn, skip_arg = _GetArgSpecFnInfo(fn)

  try:

    if six.PY2:
      argspec = inspect.getargspec(fn)
      args = argspec.args[1:] if skip_arg else argspec.args
      annotations = getattr(fn, '__annotations__', None)
      fullspec = _FullArgSpec(args, *argspec[1:], annotations=annotations)
    else:
      argspec = inspect.getfullargspec(fn)
      args = argspec.args[1:] if skip_arg else argspec.args
      fullspec = _FullArgSpec(args, *argspec[1:])

  except TypeError:
    # If we can't get the argspec, how do we know if the fn should take args?
    # 1. If it's a builtin, it can take args.
    # 2. If it's an implicit __init__ function (a 'slot wrapper'), take no args.
    # Are there other cases?
    if inspect.isbuiltin(fn):
      return _FullArgSpec(varargs='vars', varkw='kwargs')
    else:
      return _FullArgSpec()

  return fullspec

def Info(component):
  """Returns a dict with information about the given component.

  The dict will have at least some of the following fields.
    type_name: The type of `component`.
    string_form: A string representation of `component`.
    file: The file in which `component` is defined.
    line: The line number at which `component` is defined.
    docstring: The docstring of `component`.
    init_docstring: The init docstring of `component`.
    class_docstring: The class docstring of `component`.
    call_docstring: The call docstring of `component`.
    length: The length of `component`.


Review comment (suggestion):
[Unrelated comment]
Heads up that yes, there are other cases, so we'll have to change this logic a little bit (not as part of this review, of course). This does not affect your change and will go in after.
The cases we originally missed are C-extensions, which inspect.signature/inspect.fullargspec cannot get the argspec for. While we can't solve this in the general case, we can add known argspecs for builtin modules. The only ones on my list to add so far are object.\_\_init\_\_ (which gets used a lot and which we already handle correctly) and type.\_\_init\_\_ (which is much less used, but still important).

Code after:
    # If the function is a bound method, we skip the `self` argument.
    is_method = inspect.ismethod(fn)
    skip_arg = is_method and fn.__self__ is not None

  return fn, skip_arg


def GetFullArgSpec(fn):
  """Returns a FullArgSpec describing the given callable."""

  fn, skip_arg = _GetArgSpecInfo(fn)

  try:
    if six.PY2:
      args, varargs, varkw, defaults = inspect.getargspec(fn)  # pylint: disable=deprecated-method
      kwonlyargs = kwonlydefaults = None
      annotations = getattr(fn, '__annotations__', None)
    else:
      (args, varargs, varkw, defaults,
       kwonlyargs, kwonlydefaults, annotations) = inspect.getfullargspec(fn)

  except TypeError:
    # If we can't get the argspec, how do we know if the fn should take args?
    # 1. If it's a builtin, it can take args.
    # 2. If it's an implicit __init__ function (a 'slot wrapper'), take no args.
    # Are there other cases?
    if inspect.isbuiltin(fn):
      return FullArgSpec(varargs='vars', varkw='kwargs')
    else:
      return FullArgSpec()

  if skip_arg and args:
    args.pop(0)  # Remove 'self' or 'cls' from the list of arguments.

  return FullArgSpec(args, varargs, varkw, defaults,
                     kwonlyargs, kwonlydefaults, annotations)


def Info(component):
  """Returns a dict with information about the given component.

  The dict will have at least some of the following fields.
    type_name: The type of `component`.
    string_form: A string representation of `component`.
    file: The file in which `component` is defined.
    line: The line number at which `component` is defined.
    docstring: The docstring of `component`.
    init_docstring: The init docstring of `component`.
    class_docstring: The class docstring of `component`.
    call_docstring: The call docstring of `component`.
    length: The length of `component`.