Chris@87: """Subset of inspect module from upstream python Chris@87: Chris@87: We use this instead of upstream because upstream inspect is slow to import, and Chris@87: significanly contributes to numpy import times. Importing this copy has almost Chris@87: no overhead. Chris@87: Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import types Chris@87: Chris@87: __all__ = ['getargspec', 'formatargspec'] Chris@87: Chris@87: # ----------------------------------------------------------- type-checking Chris@87: def ismethod(object): Chris@87: """Return true if the object is an instance method. Chris@87: Chris@87: Instance method objects provide these attributes: Chris@87: __doc__ documentation string Chris@87: __name__ name with which this method was defined Chris@87: im_class class object in which this method belongs Chris@87: im_func function object containing implementation of method Chris@87: im_self instance to which this method is bound, or None""" Chris@87: return isinstance(object, types.MethodType) Chris@87: Chris@87: def isfunction(object): Chris@87: """Return true if the object is a user-defined function. Chris@87: Chris@87: Function objects provide these attributes: Chris@87: __doc__ documentation string Chris@87: __name__ name with which this function was defined Chris@87: func_code code object containing compiled function bytecode Chris@87: func_defaults tuple of any default values for arguments Chris@87: func_doc (same as __doc__) Chris@87: func_globals global namespace in which this function was defined Chris@87: func_name (same as __name__)""" Chris@87: return isinstance(object, types.FunctionType) Chris@87: Chris@87: def iscode(object): Chris@87: """Return true if the object is a code object. Chris@87: Chris@87: Code objects provide these attributes: Chris@87: co_argcount number of arguments (not including * or ** args) Chris@87: co_code string of raw compiled bytecode Chris@87: co_consts tuple of constants used in the bytecode Chris@87: co_filename name of file in which this code object was created Chris@87: co_firstlineno number of first line in Python source code Chris@87: co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg Chris@87: co_lnotab encoded mapping of line numbers to bytecode indices Chris@87: co_name name with which this code object was defined Chris@87: co_names tuple of names of local variables Chris@87: co_nlocals number of local variables Chris@87: co_stacksize virtual machine stack space required Chris@87: co_varnames tuple of names of arguments and local variables""" Chris@87: return isinstance(object, types.CodeType) Chris@87: Chris@87: # ------------------------------------------------ argument list extraction Chris@87: # These constants are from Python's compile.h. Chris@87: CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8 Chris@87: Chris@87: def getargs(co): Chris@87: """Get information about the arguments accepted by a code object. Chris@87: Chris@87: Three things are returned: (args, varargs, varkw), where 'args' is Chris@87: a list of argument names (possibly containing nested lists), and Chris@87: 'varargs' and 'varkw' are the names of the * and ** arguments or None.""" Chris@87: Chris@87: if not iscode(co): Chris@87: raise TypeError('arg is not a code object') Chris@87: Chris@87: code = co.co_code Chris@87: nargs = co.co_argcount Chris@87: names = co.co_varnames Chris@87: args = list(names[:nargs]) Chris@87: step = 0 Chris@87: Chris@87: # The following acrobatics are for anonymous (tuple) arguments. Chris@87: for i in range(nargs): Chris@87: if args[i][:1] in ['', '.']: Chris@87: stack, remain, count = [], [], [] Chris@87: while step < len(code): Chris@87: op = ord(code[step]) Chris@87: step = step + 1 Chris@87: if op >= dis.HAVE_ARGUMENT: Chris@87: opname = dis.opname[op] Chris@87: value = ord(code[step]) + ord(code[step+1])*256 Chris@87: step = step + 2 Chris@87: if opname in ['UNPACK_TUPLE', 'UNPACK_SEQUENCE']: Chris@87: remain.append(value) Chris@87: count.append(value) Chris@87: elif opname == 'STORE_FAST': Chris@87: stack.append(names[value]) Chris@87: Chris@87: # Special case for sublists of length 1: def foo((bar)) Chris@87: # doesn't generate the UNPACK_TUPLE bytecode, so if Chris@87: # `remain` is empty here, we have such a sublist. Chris@87: if not remain: Chris@87: stack[0] = [stack[0]] Chris@87: break Chris@87: else: Chris@87: remain[-1] = remain[-1] - 1 Chris@87: while remain[-1] == 0: Chris@87: remain.pop() Chris@87: size = count.pop() Chris@87: stack[-size:] = [stack[-size:]] Chris@87: if not remain: break Chris@87: remain[-1] = remain[-1] - 1 Chris@87: if not remain: break Chris@87: args[i] = stack[0] Chris@87: Chris@87: varargs = None Chris@87: if co.co_flags & CO_VARARGS: Chris@87: varargs = co.co_varnames[nargs] Chris@87: nargs = nargs + 1 Chris@87: varkw = None Chris@87: if co.co_flags & CO_VARKEYWORDS: Chris@87: varkw = co.co_varnames[nargs] Chris@87: return args, varargs, varkw Chris@87: Chris@87: def getargspec(func): Chris@87: """Get the names and default values of a function's arguments. Chris@87: Chris@87: A tuple of four things is returned: (args, varargs, varkw, defaults). Chris@87: 'args' is a list of the argument names (it may contain nested lists). Chris@87: 'varargs' and 'varkw' are the names of the * and ** arguments or None. Chris@87: 'defaults' is an n-tuple of the default values of the last n arguments. Chris@87: """ Chris@87: Chris@87: if ismethod(func): Chris@87: func = func.__func__ Chris@87: if not isfunction(func): Chris@87: raise TypeError('arg is not a Python function') Chris@87: args, varargs, varkw = getargs(func.__code__) Chris@87: return args, varargs, varkw, func.__defaults__ Chris@87: Chris@87: def getargvalues(frame): Chris@87: """Get information about arguments passed into a particular frame. Chris@87: Chris@87: A tuple of four things is returned: (args, varargs, varkw, locals). Chris@87: 'args' is a list of the argument names (it may contain nested lists). Chris@87: 'varargs' and 'varkw' are the names of the * and ** arguments or None. Chris@87: 'locals' is the locals dictionary of the given frame.""" Chris@87: args, varargs, varkw = getargs(frame.f_code) Chris@87: return args, varargs, varkw, frame.f_locals Chris@87: Chris@87: def joinseq(seq): Chris@87: if len(seq) == 1: Chris@87: return '(' + seq[0] + ',)' Chris@87: else: Chris@87: return '(' + ', '.join(seq) + ')' Chris@87: Chris@87: def strseq(object, convert, join=joinseq): Chris@87: """Recursively walk a sequence, stringifying each element.""" Chris@87: if type(object) in [list, tuple]: Chris@87: return join([strseq(_o, convert, join) for _o in object]) Chris@87: else: Chris@87: return convert(object) Chris@87: Chris@87: def formatargspec(args, varargs=None, varkw=None, defaults=None, Chris@87: formatarg=str, Chris@87: formatvarargs=lambda name: '*' + name, Chris@87: formatvarkw=lambda name: '**' + name, Chris@87: formatvalue=lambda value: '=' + repr(value), Chris@87: join=joinseq): Chris@87: """Format an argument spec from the 4 values returned by getargspec. Chris@87: Chris@87: The first four arguments are (args, varargs, varkw, defaults). The Chris@87: other four arguments are the corresponding optional formatting functions Chris@87: that are called to turn names and values into strings. The ninth Chris@87: argument is an optional function to format the sequence of arguments.""" Chris@87: specs = [] Chris@87: if defaults: Chris@87: firstdefault = len(args) - len(defaults) Chris@87: for i in range(len(args)): Chris@87: spec = strseq(args[i], formatarg, join) Chris@87: if defaults and i >= firstdefault: Chris@87: spec = spec + formatvalue(defaults[i - firstdefault]) Chris@87: specs.append(spec) Chris@87: if varargs is not None: Chris@87: specs.append(formatvarargs(varargs)) Chris@87: if varkw is not None: Chris@87: specs.append(formatvarkw(varkw)) Chris@87: return '(' + ', '.join(specs) + ')' Chris@87: Chris@87: def formatargvalues(args, varargs, varkw, locals, Chris@87: formatarg=str, Chris@87: formatvarargs=lambda name: '*' + name, Chris@87: formatvarkw=lambda name: '**' + name, Chris@87: formatvalue=lambda value: '=' + repr(value), Chris@87: join=joinseq): Chris@87: """Format an argument spec from the 4 values returned by getargvalues. Chris@87: Chris@87: The first four arguments are (args, varargs, varkw, locals). The Chris@87: next four arguments are the corresponding optional formatting functions Chris@87: that are called to turn names and values into strings. The ninth Chris@87: argument is an optional function to format the sequence of arguments.""" Chris@87: def convert(name, locals=locals, Chris@87: formatarg=formatarg, formatvalue=formatvalue): Chris@87: return formatarg(name) + formatvalue(locals[name]) Chris@87: specs = [] Chris@87: for i in range(len(args)): Chris@87: specs.append(strseq(args[i], convert, join)) Chris@87: if varargs: Chris@87: specs.append(formatvarargs(varargs) + formatvalue(locals[varargs])) Chris@87: if varkw: Chris@87: specs.append(formatvarkw(varkw) + formatvalue(locals[varkw])) Chris@87: return '(' + string.join(specs, ', ') + ')' Chris@87: Chris@87: if __name__ == '__main__': Chris@87: import inspect Chris@87: def foo(x, y, z=None): Chris@87: return None Chris@87: Chris@87: print(inspect.getargs(foo.__code__)) Chris@87: print(getargs(foo.__code__)) Chris@87: Chris@87: print(inspect.getargspec(foo)) Chris@87: print(getargspec(foo)) Chris@87: Chris@87: print(inspect.formatargspec(*inspect.getargspec(foo))) Chris@87: print(formatargspec(*getargspec(foo)))