@classmethod
  def callable_info(cls, f):
    '''Info about a callable.
    
    The results are cached for efficiency.
    
    :param f:
    :type  f: callable
    :rtype: frozendict
    '''
    if not callable(f):
      return None
    
    if isinstance(f, FunctionType):
      # in case of ensure_va_kwa
      try:
        f = f.wrapped_func
      except __HOLE__:
        pass
    
    cache_key = callable_cache_key(f)
    
    try:
      return cls._info_cache[cache_key]
    except KeyError:
      pass
    
    is_method, args, varargs, varkw, defaults = cls.getargspec(f)
    
    _args = []
    args_len = len(args)
    defaults_len = 0
    
    if defaults is not None:
      defaults_len = len(defaults)
    
    for i,n in enumerate(args):
      default_index = i-(args_len-defaults_len)
      v = Undefined
      if default_index > -1:
        v = defaults[default_index]
      _args.append((n, v))
    
    info = frozendict({
      'name':f.func_name,
      'args':tuple(_args),
      'varargs':bool(varargs),
      'varkw':bool(varkw),
      'method':is_method
    })
    
    cls._info_cache[cache_key] = info
    return info
dataset/ETHPy150Open rsms/smisk/lib/smisk/util/introspect.py/introspect.callable_info