--- /dev/null
+# -*- coding: iso-8859-1 -*-\r
+"""Get useful information from live Python objects.\r
+\r
+This module encapsulates the interface provided by the internal special\r
+attributes (func_*, co_*, im_*, tb_*, etc.) in a friendlier fashion.\r
+It also provides some help for examining source code and class layout.\r
+\r
+Here are some of the useful functions provided by this module:\r
+\r
+ ismodule(), isclass(), ismethod(), isfunction(), isgeneratorfunction(),\r
+ isgenerator(), istraceback(), isframe(), iscode(), isbuiltin(),\r
+ isroutine() - check object types\r
+ getmembers() - get members of an object that satisfy a given condition\r
+\r
+ getfile(), getsourcefile(), getsource() - find an object's source code\r
+ getdoc(), getcomments() - get documentation on an object\r
+ getmodule() - determine the module that an object came from\r
+ getclasstree() - arrange classes so as to represent their hierarchy\r
+\r
+ getargspec(), getargvalues(), getcallargs() - get info about function arguments\r
+ formatargspec(), formatargvalues() - format an argument spec\r
+ getouterframes(), getinnerframes() - get info about frames\r
+ currentframe() - get the current stack frame\r
+ stack(), trace() - get info about frames on the stack or in a traceback\r
+"""\r
+\r
+# This module is in the public domain. No warranties.\r
+\r
+__author__ = 'Ka-Ping Yee <ping@lfw.org>'\r
+__date__ = '1 Jan 2001'\r
+\r
+import sys\r
+import os\r
+import types\r
+import string\r
+import re\r
+import dis\r
+import imp\r
+import tokenize\r
+import linecache\r
+from operator import attrgetter\r
+from collections import namedtuple\r
+\r
+# These constants are from Include/code.h.\r
+CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 0x1, 0x2, 0x4, 0x8\r
+CO_NESTED, CO_GENERATOR, CO_NOFREE = 0x10, 0x20, 0x40\r
+# See Include/object.h\r
+TPFLAGS_IS_ABSTRACT = 1 << 20\r
+\r
+# ----------------------------------------------------------- type-checking\r
+def ismodule(object):\r
+ """Return true if the object is a module.\r
+\r
+ Module objects provide these attributes:\r
+ __doc__ documentation string\r
+ __file__ filename (missing for built-in modules)"""\r
+ return isinstance(object, types.ModuleType)\r
+\r
+def isclass(object):\r
+ """Return true if the object is a class.\r
+\r
+ Class objects provide these attributes:\r
+ __doc__ documentation string\r
+ __module__ name of module in which this class was defined"""\r
+ return isinstance(object, (type, types.ClassType))\r
+\r
+def ismethod(object):\r
+ """Return true if the object is an instance method.\r
+\r
+ Instance method objects provide these attributes:\r
+ __doc__ documentation string\r
+ __name__ name with which this method was defined\r
+ im_class class object in which this method belongs\r
+ im_func function object containing implementation of method\r
+ im_self instance to which this method is bound, or None"""\r
+ return isinstance(object, types.MethodType)\r
+\r
+def ismethoddescriptor(object):\r
+ """Return true if the object is a method descriptor.\r
+\r
+ But not if ismethod() or isclass() or isfunction() are true.\r
+\r
+ This is new in Python 2.2, and, for example, is true of int.__add__.\r
+ An object passing this test has a __get__ attribute but not a __set__\r
+ attribute, but beyond that the set of attributes varies. __name__ is\r
+ usually sensible, and __doc__ often is.\r
+\r
+ Methods implemented via descriptors that also pass one of the other\r
+ tests return false from the ismethoddescriptor() test, simply because\r
+ the other tests promise more -- you can, e.g., count on having the\r
+ im_func attribute (etc) when an object passes ismethod()."""\r
+ return (hasattr(object, "__get__")\r
+ and not hasattr(object, "__set__") # else it's a data descriptor\r
+ and not ismethod(object) # mutual exclusion\r
+ and not isfunction(object)\r
+ and not isclass(object))\r
+\r
+def isdatadescriptor(object):\r
+ """Return true if the object is a data descriptor.\r
+\r
+ Data descriptors have both a __get__ and a __set__ attribute. Examples are\r
+ properties (defined in Python) and getsets and members (defined in C).\r
+ Typically, data descriptors will also have __name__ and __doc__ attributes\r
+ (properties, getsets, and members have both of these attributes), but this\r
+ is not guaranteed."""\r
+ return (hasattr(object, "__set__") and hasattr(object, "__get__"))\r
+\r
+if hasattr(types, 'MemberDescriptorType'):\r
+ # CPython and equivalent\r
+ def ismemberdescriptor(object):\r
+ """Return true if the object is a member descriptor.\r
+\r
+ Member descriptors are specialized descriptors defined in extension\r
+ modules."""\r
+ return isinstance(object, types.MemberDescriptorType)\r
+else:\r
+ # Other implementations\r
+ def ismemberdescriptor(object):\r
+ """Return true if the object is a member descriptor.\r
+\r
+ Member descriptors are specialized descriptors defined in extension\r
+ modules."""\r
+ return False\r
+\r
+if hasattr(types, 'GetSetDescriptorType'):\r
+ # CPython and equivalent\r
+ def isgetsetdescriptor(object):\r
+ """Return true if the object is a getset descriptor.\r
+\r
+ getset descriptors are specialized descriptors defined in extension\r
+ modules."""\r
+ return isinstance(object, types.GetSetDescriptorType)\r
+else:\r
+ # Other implementations\r
+ def isgetsetdescriptor(object):\r
+ """Return true if the object is a getset descriptor.\r
+\r
+ getset descriptors are specialized descriptors defined in extension\r
+ modules."""\r
+ return False\r
+\r
+def isfunction(object):\r
+ """Return true if the object is a user-defined function.\r
+\r
+ Function objects provide these attributes:\r
+ __doc__ documentation string\r
+ __name__ name with which this function was defined\r
+ func_code code object containing compiled function bytecode\r
+ func_defaults tuple of any default values for arguments\r
+ func_doc (same as __doc__)\r
+ func_globals global namespace in which this function was defined\r
+ func_name (same as __name__)"""\r
+ return isinstance(object, types.FunctionType)\r
+\r
+def isgeneratorfunction(object):\r
+ """Return true if the object is a user-defined generator function.\r
+\r
+ Generator function objects provides same attributes as functions.\r
+\r
+ See help(isfunction) for attributes listing."""\r
+ return bool((isfunction(object) or ismethod(object)) and\r
+ object.func_code.co_flags & CO_GENERATOR)\r
+\r
+def isgenerator(object):\r
+ """Return true if the object is a generator.\r
+\r
+ Generator objects provide these attributes:\r
+ __iter__ defined to support iteration over container\r
+ close raises a new GeneratorExit exception inside the\r
+ generator to terminate the iteration\r
+ gi_code code object\r
+ gi_frame frame object or possibly None once the generator has\r
+ been exhausted\r
+ gi_running set to 1 when generator is executing, 0 otherwise\r
+ next return the next item from the container\r
+ send resumes the generator and "sends" a value that becomes\r
+ the result of the current yield-expression\r
+ throw used to raise an exception inside the generator"""\r
+ return isinstance(object, types.GeneratorType)\r
+\r
+def istraceback(object):\r
+ """Return true if the object is a traceback.\r
+\r
+ Traceback objects provide these attributes:\r
+ tb_frame frame object at this level\r
+ tb_lasti index of last attempted instruction in bytecode\r
+ tb_lineno current line number in Python source code\r
+ tb_next next inner traceback object (called by this level)"""\r
+ return isinstance(object, types.TracebackType)\r
+\r
+def isframe(object):\r
+ """Return true if the object is a frame object.\r
+\r
+ Frame objects provide these attributes:\r
+ f_back next outer frame object (this frame's caller)\r
+ f_builtins built-in namespace seen by this frame\r
+ f_code code object being executed in this frame\r
+ f_exc_traceback traceback if raised in this frame, or None\r
+ f_exc_type exception type if raised in this frame, or None\r
+ f_exc_value exception value if raised in this frame, or None\r
+ f_globals global namespace seen by this frame\r
+ f_lasti index of last attempted instruction in bytecode\r
+ f_lineno current line number in Python source code\r
+ f_locals local namespace seen by this frame\r
+ f_restricted 0 or 1 if frame is in restricted execution mode\r
+ f_trace tracing function for this frame, or None"""\r
+ return isinstance(object, types.FrameType)\r
+\r
+def iscode(object):\r
+ """Return true if the object is a code object.\r
+\r
+ Code objects provide these attributes:\r
+ co_argcount number of arguments (not including * or ** args)\r
+ co_code string of raw compiled bytecode\r
+ co_consts tuple of constants used in the bytecode\r
+ co_filename name of file in which this code object was created\r
+ co_firstlineno number of first line in Python source code\r
+ co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg\r
+ co_lnotab encoded mapping of line numbers to bytecode indices\r
+ co_name name with which this code object was defined\r
+ co_names tuple of names of local variables\r
+ co_nlocals number of local variables\r
+ co_stacksize virtual machine stack space required\r
+ co_varnames tuple of names of arguments and local variables"""\r
+ return isinstance(object, types.CodeType)\r
+\r
+def isbuiltin(object):\r
+ """Return true if the object is a built-in function or method.\r
+\r
+ Built-in functions and methods provide these attributes:\r
+ __doc__ documentation string\r
+ __name__ original name of this function or method\r
+ __self__ instance to which a method is bound, or None"""\r
+ return isinstance(object, types.BuiltinFunctionType)\r
+\r
+def isroutine(object):\r
+ """Return true if the object is any kind of function or method."""\r
+ return (isbuiltin(object)\r
+ or isfunction(object)\r
+ or ismethod(object)\r
+ or ismethoddescriptor(object))\r
+\r
+def isabstract(object):\r
+ """Return true if the object is an abstract base class (ABC)."""\r
+ return bool(isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT)\r
+\r
+def getmembers(object, predicate=None):\r
+ """Return all members of an object as (name, value) pairs sorted by name.\r
+ Optionally, only return members that satisfy a given predicate."""\r
+ results = []\r
+ for key in dir(object):\r
+ try:\r
+ value = getattr(object, key)\r
+ except AttributeError:\r
+ continue\r
+ if not predicate or predicate(value):\r
+ results.append((key, value))\r
+ results.sort()\r
+ return results\r
+\r
+Attribute = namedtuple('Attribute', 'name kind defining_class object')\r
+\r
+def classify_class_attrs(cls):\r
+ """Return list of attribute-descriptor tuples.\r
+\r
+ For each name in dir(cls), the return list contains a 4-tuple\r
+ with these elements:\r
+\r
+ 0. The name (a string).\r
+\r
+ 1. The kind of attribute this is, one of these strings:\r
+ 'class method' created via classmethod()\r
+ 'static method' created via staticmethod()\r
+ 'property' created via property()\r
+ 'method' any other flavor of method\r
+ 'data' not a method\r
+\r
+ 2. The class which defined this attribute (a class).\r
+\r
+ 3. The object as obtained directly from the defining class's\r
+ __dict__, not via getattr. This is especially important for\r
+ data attributes: C.data is just a data object, but\r
+ C.__dict__['data'] may be a data descriptor with additional\r
+ info, like a __doc__ string.\r
+ """\r
+\r
+ mro = getmro(cls)\r
+ names = dir(cls)\r
+ result = []\r
+ for name in names:\r
+ # Get the object associated with the name, and where it was defined.\r
+ # Getting an obj from the __dict__ sometimes reveals more than\r
+ # using getattr. Static and class methods are dramatic examples.\r
+ # Furthermore, some objects may raise an Exception when fetched with\r
+ # getattr(). This is the case with some descriptors (bug #1785).\r
+ # Thus, we only use getattr() as a last resort.\r
+ homecls = None\r
+ for base in (cls,) + mro:\r
+ if name in base.__dict__:\r
+ obj = base.__dict__[name]\r
+ homecls = base\r
+ break\r
+ else:\r
+ obj = getattr(cls, name)\r
+ homecls = getattr(obj, "__objclass__", homecls)\r
+\r
+ # Classify the object.\r
+ if isinstance(obj, staticmethod):\r
+ kind = "static method"\r
+ elif isinstance(obj, classmethod):\r
+ kind = "class method"\r
+ elif isinstance(obj, property):\r
+ kind = "property"\r
+ elif ismethoddescriptor(obj):\r
+ kind = "method"\r
+ elif isdatadescriptor(obj):\r
+ kind = "data"\r
+ else:\r
+ obj_via_getattr = getattr(cls, name)\r
+ if (ismethod(obj_via_getattr) or\r
+ ismethoddescriptor(obj_via_getattr)):\r
+ kind = "method"\r
+ else:\r
+ kind = "data"\r
+ obj = obj_via_getattr\r
+\r
+ result.append(Attribute(name, kind, homecls, obj))\r
+\r
+ return result\r
+\r
+# ----------------------------------------------------------- class helpers\r
+def _searchbases(cls, accum):\r
+ # Simulate the "classic class" search order.\r
+ if cls in accum:\r
+ return\r
+ accum.append(cls)\r
+ for base in cls.__bases__:\r
+ _searchbases(base, accum)\r
+\r
+def getmro(cls):\r
+ "Return tuple of base classes (including cls) in method resolution order."\r
+ if hasattr(cls, "__mro__"):\r
+ return cls.__mro__\r
+ else:\r
+ result = []\r
+ _searchbases(cls, result)\r
+ return tuple(result)\r
+\r
+# -------------------------------------------------- source code extraction\r
+def indentsize(line):\r
+ """Return the indent size, in spaces, at the start of a line of text."""\r
+ expline = string.expandtabs(line)\r
+ return len(expline) - len(string.lstrip(expline))\r
+\r
+def getdoc(object):\r
+ """Get the documentation string for an object.\r
+\r
+ All tabs are expanded to spaces. To clean up docstrings that are\r
+ indented to line up with blocks of code, any whitespace than can be\r
+ uniformly removed from the second line onwards is removed."""\r
+ try:\r
+ doc = object.__doc__\r
+ except AttributeError:\r
+ return None\r
+ if not isinstance(doc, types.StringTypes):\r
+ return None\r
+ return cleandoc(doc)\r
+\r
+def cleandoc(doc):\r
+ """Clean up indentation from docstrings.\r
+\r
+ Any whitespace that can be uniformly removed from the second line\r
+ onwards is removed."""\r
+ try:\r
+ lines = string.split(string.expandtabs(doc), '\n')\r
+ except UnicodeError:\r
+ return None\r
+ else:\r
+ # Find minimum indentation of any non-blank lines after first line.\r
+ margin = sys.maxint\r
+ for line in lines[1:]:\r
+ content = len(string.lstrip(line))\r
+ if content:\r
+ indent = len(line) - content\r
+ margin = min(margin, indent)\r
+ # Remove indentation.\r
+ if lines:\r
+ lines[0] = lines[0].lstrip()\r
+ if margin < sys.maxint:\r
+ for i in range(1, len(lines)): lines[i] = lines[i][margin:]\r
+ # Remove any trailing or leading blank lines.\r
+ while lines and not lines[-1]:\r
+ lines.pop()\r
+ while lines and not lines[0]:\r
+ lines.pop(0)\r
+ return string.join(lines, '\n')\r
+\r
+def getfile(object):\r
+ """Work out which source or compiled file an object was defined in."""\r
+ if ismodule(object):\r
+ if hasattr(object, '__file__'):\r
+ return object.__file__\r
+ raise TypeError('{!r} is a built-in module'.format(object))\r
+ if isclass(object):\r
+ object = sys.modules.get(object.__module__)\r
+ if hasattr(object, '__file__'):\r
+ return object.__file__\r
+ raise TypeError('{!r} is a built-in class'.format(object))\r
+ if ismethod(object):\r
+ object = object.im_func\r
+ if isfunction(object):\r
+ object = object.func_code\r
+ if istraceback(object):\r
+ object = object.tb_frame\r
+ if isframe(object):\r
+ object = object.f_code\r
+ if iscode(object):\r
+ return object.co_filename\r
+ raise TypeError('{!r} is not a module, class, method, '\r
+ 'function, traceback, frame, or code object'.format(object))\r
+\r
+ModuleInfo = namedtuple('ModuleInfo', 'name suffix mode module_type')\r
+\r
+def getmoduleinfo(path):\r
+ """Get the module name, suffix, mode, and module type for a given file."""\r
+ filename = os.path.basename(path)\r
+ suffixes = map(lambda info:\r
+ (-len(info[0]), info[0], info[1], info[2]),\r
+ imp.get_suffixes())\r
+ suffixes.sort() # try longest suffixes first, in case they overlap\r
+ for neglen, suffix, mode, mtype in suffixes:\r
+ if filename[neglen:] == suffix:\r
+ return ModuleInfo(filename[:neglen], suffix, mode, mtype)\r
+\r
+def getmodulename(path):\r
+ """Return the module name for a given file, or None."""\r
+ info = getmoduleinfo(path)\r
+ if info: return info[0]\r
+\r
+def getsourcefile(object):\r
+ """Return the filename that can be used to locate an object's source.\r
+ Return None if no way can be identified to get the source.\r
+ """\r
+ filename = getfile(object)\r
+ if string.lower(filename[-4:]) in ('.pyc', '.pyo'):\r
+ filename = filename[:-4] + '.py'\r
+ for suffix, mode, kind in imp.get_suffixes():\r
+ if 'b' in mode and string.lower(filename[-len(suffix):]) == suffix:\r
+ # Looks like a binary file. We want to only return a text file.\r
+ return None\r
+ if os.path.exists(filename):\r
+ return filename\r
+ # only return a non-existent filename if the module has a PEP 302 loader\r
+ if hasattr(getmodule(object, filename), '__loader__'):\r
+ return filename\r
+ # or it is in the linecache\r
+ if filename in linecache.cache:\r
+ return filename\r
+\r
+def getabsfile(object, _filename=None):\r
+ """Return an absolute path to the source or compiled file for an object.\r
+\r
+ The idea is for each object to have a unique origin, so this routine\r
+ normalizes the result as much as possible."""\r
+ if _filename is None:\r
+ _filename = getsourcefile(object) or getfile(object)\r
+ return os.path.normcase(os.path.abspath(_filename))\r
+\r
+modulesbyfile = {}\r
+_filesbymodname = {}\r
+\r
+def getmodule(object, _filename=None):\r
+ """Return the module an object was defined in, or None if not found."""\r
+ if ismodule(object):\r
+ return object\r
+ if hasattr(object, '__module__'):\r
+ return sys.modules.get(object.__module__)\r
+ # Try the filename to modulename cache\r
+ if _filename is not None and _filename in modulesbyfile:\r
+ return sys.modules.get(modulesbyfile[_filename])\r
+ # Try the cache again with the absolute file name\r
+ try:\r
+ file = getabsfile(object, _filename)\r
+ except TypeError:\r
+ return None\r
+ if file in modulesbyfile:\r
+ return sys.modules.get(modulesbyfile[file])\r
+ # Update the filename to module name cache and check yet again\r
+ # Copy sys.modules in order to cope with changes while iterating\r
+ for modname, module in sys.modules.items():\r
+ if ismodule(module) and hasattr(module, '__file__'):\r
+ f = module.__file__\r
+ if f == _filesbymodname.get(modname, None):\r
+ # Have already mapped this module, so skip it\r
+ continue\r
+ _filesbymodname[modname] = f\r
+ f = getabsfile(module)\r
+ # Always map to the name the module knows itself by\r
+ modulesbyfile[f] = modulesbyfile[\r
+ os.path.realpath(f)] = module.__name__\r
+ if file in modulesbyfile:\r
+ return sys.modules.get(modulesbyfile[file])\r
+ # Check the main module\r
+ main = sys.modules['__main__']\r
+ if not hasattr(object, '__name__'):\r
+ return None\r
+ if hasattr(main, object.__name__):\r
+ mainobject = getattr(main, object.__name__)\r
+ if mainobject is object:\r
+ return main\r
+ # Check builtins\r
+ builtin = sys.modules['__builtin__']\r
+ if hasattr(builtin, object.__name__):\r
+ builtinobject = getattr(builtin, object.__name__)\r
+ if builtinobject is object:\r
+ return builtin\r
+\r
+def findsource(object):\r
+ """Return the entire source file and starting line number for an object.\r
+\r
+ The argument may be a module, class, method, function, traceback, frame,\r
+ or code object. The source code is returned as a list of all the lines\r
+ in the file and the line number indexes a line in that list. An IOError\r
+ is raised if the source code cannot be retrieved."""\r
+\r
+ file = getfile(object)\r
+ sourcefile = getsourcefile(object)\r
+ if not sourcefile and file[:1] + file[-1:] != '<>':\r
+ raise IOError('source code not available')\r
+ file = sourcefile if sourcefile else file\r
+\r
+ module = getmodule(object, file)\r
+ if module:\r
+ lines = linecache.getlines(file, module.__dict__)\r
+ else:\r
+ lines = linecache.getlines(file)\r
+ if not lines:\r
+ raise IOError('could not get source code')\r
+\r
+ if ismodule(object):\r
+ return lines, 0\r
+\r
+ if isclass(object):\r
+ name = object.__name__\r
+ pat = re.compile(r'^(\s*)class\s*' + name + r'\b')\r
+ # make some effort to find the best matching class definition:\r
+ # use the one with the least indentation, which is the one\r
+ # that's most probably not inside a function definition.\r
+ candidates = []\r
+ for i in range(len(lines)):\r
+ match = pat.match(lines[i])\r
+ if match:\r
+ # if it's at toplevel, it's already the best one\r
+ if lines[i][0] == 'c':\r
+ return lines, i\r
+ # else add whitespace to candidate list\r
+ candidates.append((match.group(1), i))\r
+ if candidates:\r
+ # this will sort by whitespace, and by line number,\r
+ # less whitespace first\r
+ candidates.sort()\r
+ return lines, candidates[0][1]\r
+ else:\r
+ raise IOError('could not find class definition')\r
+\r
+ if ismethod(object):\r
+ object = object.im_func\r
+ if isfunction(object):\r
+ object = object.func_code\r
+ if istraceback(object):\r
+ object = object.tb_frame\r
+ if isframe(object):\r
+ object = object.f_code\r
+ if iscode(object):\r
+ if not hasattr(object, 'co_firstlineno'):\r
+ raise IOError('could not find function definition')\r
+ lnum = object.co_firstlineno - 1\r
+ pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')\r
+ while lnum > 0:\r
+ if pat.match(lines[lnum]): break\r
+ lnum = lnum - 1\r
+ return lines, lnum\r
+ raise IOError('could not find code object')\r
+\r
+def getcomments(object):\r
+ """Get lines of comments immediately preceding an object's source code.\r
+\r
+ Returns None when source can't be found.\r
+ """\r
+ try:\r
+ lines, lnum = findsource(object)\r
+ except (IOError, TypeError):\r
+ return None\r
+\r
+ if ismodule(object):\r
+ # Look for a comment block at the top of the file.\r
+ start = 0\r
+ if lines and lines[0][:2] == '#!': start = 1\r
+ while start < len(lines) and string.strip(lines[start]) in ('', '#'):\r
+ start = start + 1\r
+ if start < len(lines) and lines[start][:1] == '#':\r
+ comments = []\r
+ end = start\r
+ while end < len(lines) and lines[end][:1] == '#':\r
+ comments.append(string.expandtabs(lines[end]))\r
+ end = end + 1\r
+ return string.join(comments, '')\r
+\r
+ # Look for a preceding block of comments at the same indentation.\r
+ elif lnum > 0:\r
+ indent = indentsize(lines[lnum])\r
+ end = lnum - 1\r
+ if end >= 0 and string.lstrip(lines[end])[:1] == '#' and \\r
+ indentsize(lines[end]) == indent:\r
+ comments = [string.lstrip(string.expandtabs(lines[end]))]\r
+ if end > 0:\r
+ end = end - 1\r
+ comment = string.lstrip(string.expandtabs(lines[end]))\r
+ while comment[:1] == '#' and indentsize(lines[end]) == indent:\r
+ comments[:0] = [comment]\r
+ end = end - 1\r
+ if end < 0: break\r
+ comment = string.lstrip(string.expandtabs(lines[end]))\r
+ while comments and string.strip(comments[0]) == '#':\r
+ comments[:1] = []\r
+ while comments and string.strip(comments[-1]) == '#':\r
+ comments[-1:] = []\r
+ return string.join(comments, '')\r
+\r
+class EndOfBlock(Exception): pass\r
+\r
+class BlockFinder:\r
+ """Provide a tokeneater() method to detect the end of a code block."""\r
+ def __init__(self):\r
+ self.indent = 0\r
+ self.islambda = False\r
+ self.started = False\r
+ self.passline = False\r
+ self.last = 1\r
+\r
+ def tokeneater(self, type, token, srow_scol, erow_ecol, line):\r
+ srow, scol = srow_scol\r
+ erow, ecol = erow_ecol\r
+ if not self.started:\r
+ # look for the first "def", "class" or "lambda"\r
+ if token in ("def", "class", "lambda"):\r
+ if token == "lambda":\r
+ self.islambda = True\r
+ self.started = True\r
+ self.passline = True # skip to the end of the line\r
+ elif type == tokenize.NEWLINE:\r
+ self.passline = False # stop skipping when a NEWLINE is seen\r
+ self.last = srow\r
+ if self.islambda: # lambdas always end at the first NEWLINE\r
+ raise EndOfBlock\r
+ elif self.passline:\r
+ pass\r
+ elif type == tokenize.INDENT:\r
+ self.indent = self.indent + 1\r
+ self.passline = True\r
+ elif type == tokenize.DEDENT:\r
+ self.indent = self.indent - 1\r
+ # the end of matching indent/dedent pairs end a block\r
+ # (note that this only works for "def"/"class" blocks,\r
+ # not e.g. for "if: else:" or "try: finally:" blocks)\r
+ if self.indent <= 0:\r
+ raise EndOfBlock\r
+ elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL):\r
+ # any other token on the same indentation level end the previous\r
+ # block as well, except the pseudo-tokens COMMENT and NL.\r
+ raise EndOfBlock\r
+\r
+def getblock(lines):\r
+ """Extract the block of code at the top of the given list of lines."""\r
+ blockfinder = BlockFinder()\r
+ try:\r
+ tokenize.tokenize(iter(lines).next, blockfinder.tokeneater)\r
+ except (EndOfBlock, IndentationError):\r
+ pass\r
+ return lines[:blockfinder.last]\r
+\r
+def getsourcelines(object):\r
+ """Return a list of source lines and starting line number for an object.\r
+\r
+ The argument may be a module, class, method, function, traceback, frame,\r
+ or code object. The source code is returned as a list of the lines\r
+ corresponding to the object and the line number indicates where in the\r
+ original source file the first line of code was found. An IOError is\r
+ raised if the source code cannot be retrieved."""\r
+ lines, lnum = findsource(object)\r
+\r
+ if ismodule(object): return lines, 0\r
+ else: return getblock(lines[lnum:]), lnum + 1\r
+\r
+def getsource(object):\r
+ """Return the text of the source code for an object.\r
+\r
+ The argument may be a module, class, method, function, traceback, frame,\r
+ or code object. The source code is returned as a single string. An\r
+ IOError is raised if the source code cannot be retrieved."""\r
+ lines, lnum = getsourcelines(object)\r
+ return string.join(lines, '')\r
+\r
+# --------------------------------------------------- class tree extraction\r
+def walktree(classes, children, parent):\r
+ """Recursive helper function for getclasstree()."""\r
+ results = []\r
+ classes.sort(key=attrgetter('__module__', '__name__'))\r
+ for c in classes:\r
+ results.append((c, c.__bases__))\r
+ if c in children:\r
+ results.append(walktree(children[c], children, c))\r
+ return results\r
+\r
+def getclasstree(classes, unique=0):\r
+ """Arrange the given list of classes into a hierarchy of nested lists.\r
+\r
+ Where a nested list appears, it contains classes derived from the class\r
+ whose entry immediately precedes the list. Each entry is a 2-tuple\r
+ containing a class and a tuple of its base classes. If the 'unique'\r
+ argument is true, exactly one entry appears in the returned structure\r
+ for each class in the given list. Otherwise, classes using multiple\r
+ inheritance and their descendants will appear multiple times."""\r
+ children = {}\r
+ roots = []\r
+ for c in classes:\r
+ if c.__bases__:\r
+ for parent in c.__bases__:\r
+ if not parent in children:\r
+ children[parent] = []\r
+ if c not in children[parent]:\r
+ children[parent].append(c)\r
+ if unique and parent in classes: break\r
+ elif c not in roots:\r
+ roots.append(c)\r
+ for parent in children:\r
+ if parent not in classes:\r
+ roots.append(parent)\r
+ return walktree(roots, children, None)\r
+\r
+# ------------------------------------------------ argument list extraction\r
+Arguments = namedtuple('Arguments', 'args varargs keywords')\r
+\r
+def getargs(co):\r
+ """Get information about the arguments accepted by a code object.\r
+\r
+ Three things are returned: (args, varargs, varkw), where 'args' is\r
+ a list of argument names (possibly containing nested lists), and\r
+ 'varargs' and 'varkw' are the names of the * and ** arguments or None."""\r
+\r
+ if not iscode(co):\r
+ raise TypeError('{!r} is not a code object'.format(co))\r
+\r
+ nargs = co.co_argcount\r
+ names = co.co_varnames\r
+ args = list(names[:nargs])\r
+ step = 0\r
+\r
+ # The following acrobatics are for anonymous (tuple) arguments.\r
+ for i in range(nargs):\r
+ if args[i][:1] in ('', '.'):\r
+ stack, remain, count = [], [], []\r
+ while step < len(co.co_code):\r
+ op = ord(co.co_code[step])\r
+ step = step + 1\r
+ if op >= dis.HAVE_ARGUMENT:\r
+ opname = dis.opname[op]\r
+ value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256\r
+ step = step + 2\r
+ if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):\r
+ remain.append(value)\r
+ count.append(value)\r
+ elif opname == 'STORE_FAST':\r
+ stack.append(names[value])\r
+\r
+ # Special case for sublists of length 1: def foo((bar))\r
+ # doesn't generate the UNPACK_TUPLE bytecode, so if\r
+ # `remain` is empty here, we have such a sublist.\r
+ if not remain:\r
+ stack[0] = [stack[0]]\r
+ break\r
+ else:\r
+ remain[-1] = remain[-1] - 1\r
+ while remain[-1] == 0:\r
+ remain.pop()\r
+ size = count.pop()\r
+ stack[-size:] = [stack[-size:]]\r
+ if not remain: break\r
+ remain[-1] = remain[-1] - 1\r
+ if not remain: break\r
+ args[i] = stack[0]\r
+\r
+ varargs = None\r
+ if co.co_flags & CO_VARARGS:\r
+ varargs = co.co_varnames[nargs]\r
+ nargs = nargs + 1\r
+ varkw = None\r
+ if co.co_flags & CO_VARKEYWORDS:\r
+ varkw = co.co_varnames[nargs]\r
+ return Arguments(args, varargs, varkw)\r
+\r
+ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')\r
+\r
+def getargspec(func):\r
+ """Get the names and default values of a function's arguments.\r
+\r
+ A tuple of four things is returned: (args, varargs, varkw, defaults).\r
+ 'args' is a list of the argument names (it may contain nested lists).\r
+ 'varargs' and 'varkw' are the names of the * and ** arguments or None.\r
+ 'defaults' is an n-tuple of the default values of the last n arguments.\r
+ """\r
+\r
+ if ismethod(func):\r
+ func = func.im_func\r
+ if not isfunction(func):\r
+ raise TypeError('{!r} is not a Python function'.format(func))\r
+ args, varargs, varkw = getargs(func.func_code)\r
+ return ArgSpec(args, varargs, varkw, func.func_defaults)\r
+\r
+ArgInfo = namedtuple('ArgInfo', 'args varargs keywords locals')\r
+\r
+def getargvalues(frame):\r
+ """Get information about arguments passed into a particular frame.\r
+\r
+ A tuple of four things is returned: (args, varargs, varkw, locals).\r
+ 'args' is a list of the argument names (it may contain nested lists).\r
+ 'varargs' and 'varkw' are the names of the * and ** arguments or None.\r
+ 'locals' is the locals dictionary of the given frame."""\r
+ args, varargs, varkw = getargs(frame.f_code)\r
+ return ArgInfo(args, varargs, varkw, frame.f_locals)\r
+\r
+def joinseq(seq):\r
+ if len(seq) == 1:\r
+ return '(' + seq[0] + ',)'\r
+ else:\r
+ return '(' + string.join(seq, ', ') + ')'\r
+\r
+def strseq(object, convert, join=joinseq):\r
+ """Recursively walk a sequence, stringifying each element."""\r
+ if type(object) in (list, tuple):\r
+ return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object))\r
+ else:\r
+ return convert(object)\r
+\r
+def formatargspec(args, varargs=None, varkw=None, defaults=None,\r
+ formatarg=str,\r
+ formatvarargs=lambda name: '*' + name,\r
+ formatvarkw=lambda name: '**' + name,\r
+ formatvalue=lambda value: '=' + repr(value),\r
+ join=joinseq):\r
+ """Format an argument spec from the 4 values returned by getargspec.\r
+\r
+ The first four arguments are (args, varargs, varkw, defaults). The\r
+ other four arguments are the corresponding optional formatting functions\r
+ that are called to turn names and values into strings. The ninth\r
+ argument is an optional function to format the sequence of arguments."""\r
+ specs = []\r
+ if defaults:\r
+ firstdefault = len(args) - len(defaults)\r
+ for i, arg in enumerate(args):\r
+ spec = strseq(arg, formatarg, join)\r
+ if defaults and i >= firstdefault:\r
+ spec = spec + formatvalue(defaults[i - firstdefault])\r
+ specs.append(spec)\r
+ if varargs is not None:\r
+ specs.append(formatvarargs(varargs))\r
+ if varkw is not None:\r
+ specs.append(formatvarkw(varkw))\r
+ return '(' + string.join(specs, ', ') + ')'\r
+\r
+def formatargvalues(args, varargs, varkw, locals,\r
+ formatarg=str,\r
+ formatvarargs=lambda name: '*' + name,\r
+ formatvarkw=lambda name: '**' + name,\r
+ formatvalue=lambda value: '=' + repr(value),\r
+ join=joinseq):\r
+ """Format an argument spec from the 4 values returned by getargvalues.\r
+\r
+ The first four arguments are (args, varargs, varkw, locals). The\r
+ next four arguments are the corresponding optional formatting functions\r
+ that are called to turn names and values into strings. The ninth\r
+ argument is an optional function to format the sequence of arguments."""\r
+ def convert(name, locals=locals,\r
+ formatarg=formatarg, formatvalue=formatvalue):\r
+ return formatarg(name) + formatvalue(locals[name])\r
+ specs = []\r
+ for i in range(len(args)):\r
+ specs.append(strseq(args[i], convert, join))\r
+ if varargs:\r
+ specs.append(formatvarargs(varargs) + formatvalue(locals[varargs]))\r
+ if varkw:\r
+ specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))\r
+ return '(' + string.join(specs, ', ') + ')'\r
+\r
+def getcallargs(func, *positional, **named):\r
+ """Get the mapping of arguments to values.\r
+\r
+ A dict is returned, with keys the function argument names (including the\r
+ names of the * and ** arguments, if any), and values the respective bound\r
+ values from 'positional' and 'named'."""\r
+ args, varargs, varkw, defaults = getargspec(func)\r
+ f_name = func.__name__\r
+ arg2value = {}\r
+\r
+ # The following closures are basically because of tuple parameter unpacking.\r
+ assigned_tuple_params = []\r
+ def assign(arg, value):\r
+ if isinstance(arg, str):\r
+ arg2value[arg] = value\r
+ else:\r
+ assigned_tuple_params.append(arg)\r
+ value = iter(value)\r
+ for i, subarg in enumerate(arg):\r
+ try:\r
+ subvalue = next(value)\r
+ except StopIteration:\r
+ raise ValueError('need more than %d %s to unpack' %\r
+ (i, 'values' if i > 1 else 'value'))\r
+ assign(subarg,subvalue)\r
+ try:\r
+ next(value)\r
+ except StopIteration:\r
+ pass\r
+ else:\r
+ raise ValueError('too many values to unpack')\r
+ def is_assigned(arg):\r
+ if isinstance(arg,str):\r
+ return arg in arg2value\r
+ return arg in assigned_tuple_params\r
+ if ismethod(func) and func.im_self is not None:\r
+ # implicit 'self' (or 'cls' for classmethods) argument\r
+ positional = (func.im_self,) + positional\r
+ num_pos = len(positional)\r
+ num_total = num_pos + len(named)\r
+ num_args = len(args)\r
+ num_defaults = len(defaults) if defaults else 0\r
+ for arg, value in zip(args, positional):\r
+ assign(arg, value)\r
+ if varargs:\r
+ if num_pos > num_args:\r
+ assign(varargs, positional[-(num_pos-num_args):])\r
+ else:\r
+ assign(varargs, ())\r
+ elif 0 < num_args < num_pos:\r
+ raise TypeError('%s() takes %s %d %s (%d given)' % (\r
+ f_name, 'at most' if defaults else 'exactly', num_args,\r
+ 'arguments' if num_args > 1 else 'argument', num_total))\r
+ elif num_args == 0 and num_total:\r
+ if varkw:\r
+ if num_pos:\r
+ # XXX: We should use num_pos, but Python also uses num_total:\r
+ raise TypeError('%s() takes exactly 0 arguments '\r
+ '(%d given)' % (f_name, num_total))\r
+ else:\r
+ raise TypeError('%s() takes no arguments (%d given)' %\r
+ (f_name, num_total))\r
+ for arg in args:\r
+ if isinstance(arg, str) and arg in named:\r
+ if is_assigned(arg):\r
+ raise TypeError("%s() got multiple values for keyword "\r
+ "argument '%s'" % (f_name, arg))\r
+ else:\r
+ assign(arg, named.pop(arg))\r
+ if defaults: # fill in any missing values with the defaults\r
+ for arg, value in zip(args[-num_defaults:], defaults):\r
+ if not is_assigned(arg):\r
+ assign(arg, value)\r
+ if varkw:\r
+ assign(varkw, named)\r
+ elif named:\r
+ unexpected = next(iter(named))\r
+ if isinstance(unexpected, unicode):\r
+ unexpected = unexpected.encode(sys.getdefaultencoding(), 'replace')\r
+ raise TypeError("%s() got an unexpected keyword argument '%s'" %\r
+ (f_name, unexpected))\r
+ unassigned = num_args - len([arg for arg in args if is_assigned(arg)])\r
+ if unassigned:\r
+ num_required = num_args - num_defaults\r
+ raise TypeError('%s() takes %s %d %s (%d given)' % (\r
+ f_name, 'at least' if defaults else 'exactly', num_required,\r
+ 'arguments' if num_required > 1 else 'argument', num_total))\r
+ return arg2value\r
+\r
+# -------------------------------------------------- stack frame extraction\r
+\r
+Traceback = namedtuple('Traceback', 'filename lineno function code_context index')\r
+\r
+def getframeinfo(frame, context=1):\r
+ """Get information about a frame or traceback object.\r
+\r
+ A tuple of five things is returned: the filename, the line number of\r
+ the current line, the function name, a list of lines of context from\r
+ the source code, and the index of the current line within that list.\r
+ The optional second argument specifies the number of lines of context\r
+ to return, which are centered around the current line."""\r
+ if istraceback(frame):\r
+ lineno = frame.tb_lineno\r
+ frame = frame.tb_frame\r
+ else:\r
+ lineno = frame.f_lineno\r
+ if not isframe(frame):\r
+ raise TypeError('{!r} is not a frame or traceback object'.format(frame))\r
+\r
+ filename = getsourcefile(frame) or getfile(frame)\r
+ if context > 0:\r
+ start = lineno - 1 - context//2\r
+ try:\r
+ lines, lnum = findsource(frame)\r
+ except IOError:\r
+ lines = index = None\r
+ else:\r
+ start = max(start, 1)\r
+ start = max(0, min(start, len(lines) - context))\r
+ lines = lines[start:start+context]\r
+ index = lineno - 1 - start\r
+ else:\r
+ lines = index = None\r
+\r
+ return Traceback(filename, lineno, frame.f_code.co_name, lines, index)\r
+\r
+def getlineno(frame):\r
+ """Get the line number from a frame object, allowing for optimization."""\r
+ # FrameType.f_lineno is now a descriptor that grovels co_lnotab\r
+ return frame.f_lineno\r
+\r
+def getouterframes(frame, context=1):\r
+ """Get a list of records for a frame and all higher (calling) frames.\r
+\r
+ Each record contains a frame object, filename, line number, function\r
+ name, a list of lines of context, and index within the context."""\r
+ framelist = []\r
+ while frame:\r
+ framelist.append((frame,) + getframeinfo(frame, context))\r
+ frame = frame.f_back\r
+ return framelist\r
+\r
+def getinnerframes(tb, context=1):\r
+ """Get a list of records for a traceback's frame and all lower frames.\r
+\r
+ Each record contains a frame object, filename, line number, function\r
+ name, a list of lines of context, and index within the context."""\r
+ framelist = []\r
+ while tb:\r
+ framelist.append((tb.tb_frame,) + getframeinfo(tb, context))\r
+ tb = tb.tb_next\r
+ return framelist\r
+\r
+if hasattr(sys, '_getframe'):\r
+ currentframe = sys._getframe\r
+else:\r
+ currentframe = lambda _=None: None\r
+\r
+def stack(context=1):\r
+ """Return a list of records for the stack above the caller's frame."""\r
+ return getouterframes(sys._getframe(1), context)\r
+\r
+def trace(context=1):\r
+ """Return a list of records for the stack below the current exception."""\r
+ return getinnerframes(sys.exc_info()[2], context)\r