+def default_exitstatfunc(s):
+ return s
+
+try:
+ SET_LINENO = dis.SET_LINENO
+ HAVE_ARGUMENT = dis.HAVE_ARGUMENT
+except AttributeError:
+ remove_set_lineno_codes = lambda x: x
+else:
+ def remove_set_lineno_codes(code):
+ result = []
+ n = len(code)
+ i = 0
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ if op >= HAVE_ARGUMENT:
+ if op != SET_LINENO:
+ result.append(code[i:i+3])
+ i = i+3
+ else:
+ result.append(c)
+ i = i+1
+ return string.join(result, '')
+
+strip_quotes = re.compile('^[\'"](.*)[\'"]$')
+
+
+def _callable_contents(obj):
+ """Return the signature contents of a callable Python object.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+
+def _object_contents(obj):
+ """Return the signature contents of any Python object.
+
+ We have to handle the case where object contains a code object
+ since it can be pickled directly.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ try:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+ except AttributeError:
+ # Should be a pickable Python object.
+ try:
+ return cPickle.dumps(obj)
+ except (cPickle.PicklingError, TypeError):
+ # This is weird, but it seems that nested classes
+ # are unpickable. The Python docs say it should
+ # always be a PicklingError, but some Python
+ # versions seem to return TypeError. Just do
+ # the best we can.
+ return str(obj)
+
+
+def _code_contents(code):
+ """Return the signature contents of a code object.
+
+ By providing direct access to the code object of the
+ function, Python makes this extremely easy. Hooray!
+
+ Unfortunately, older versions of Python include line
+ number indications in the compiled byte code. Boo!
+ So we remove the line number byte codes to prevent
+ recompilations from moving a Python function.
+ """
+
+ contents = []
+
+ # The code contents depends on the number of local variables
+ # but not their actual names.
+ contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
+ try:
+ contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ contents.append(",0,0")
+
+ # The code contents depends on any constants accessed by the
+ # function. Note that we have to call _object_contents on each
+ # constants because the code object of nested functions can
+ # show-up among the constants.
+ #
+ # Note that we also always ignore the first entry of co_consts
+ # which contains the function doc string. We assume that the
+ # function does not access its doc string.
+ contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
+
+ # The code contents depends on the variable names used to
+ # accessed global variable, as changing the variable name changes
+ # the variable actually accessed and therefore changes the
+ # function result.
+ contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
+
+
+ # The code contents depends on its actual code!!!
+ contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
+
+ return string.join(contents, '')
+
+
+def _function_contents(func):
+ """Return the signature contents of a function."""
+
+ contents = [_code_contents(func.func_code)]
+
+ # The function contents depends on the value of defaults arguments
+ if func.func_defaults:
+ contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
+ else:
+ contents.append(',()')
+
+ # The function contents depends on the closure captured cell values.
+ try:
+ closure = func.func_closure or []
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ closure = []
+
+ #xxx = [_object_contents(x.cell_contents) for x in closure]
+ try:
+ xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+ except AttributeError:
+ xxx = []
+ contents.append(',(' + string.join(xxx, ',') + ')')
+
+ return string.join(contents, '')
+
+