+++ /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 interation 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.\r
- # Getting an obj from the __dict__ sometimes reveals more than\r
- # using getattr. Static and class methods are dramatic examples.\r
- if name in cls.__dict__:\r
- obj = cls.__dict__[name]\r
- else:\r
- obj = getattr(cls, name)\r
-\r
- # Figure out where it was defined.\r
- homecls = getattr(obj, "__objclass__", None)\r
- if homecls is None:\r
- # search the dicts.\r
- for base in mro:\r
- if name in base.__dict__:\r
- homecls = base\r
- break\r
-\r
- # Get the object again, in order to get it from the defining\r
- # __dict__ instead of via getattr (if possible).\r
- if homecls is not None and name in homecls.__dict__:\r
- obj = homecls.__dict__[name]\r
-\r
- # Also get the object via getattr.\r
- obj_via_getattr = getattr(cls, name)\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 (ismethod(obj_via_getattr) or\r
- ismethoddescriptor(obj_via_getattr)):\r
- kind = "method"\r
- else:\r
- kind = "data"\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
- file = getsourcefile(object)\r
- if not file:\r
- raise IOError('source code not available')\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
- 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