# Copyright (C) Boris Kats 2011 - 2012.
# inspired and based on Yaniv Aknin "objwalk" http://code.activestate.com/recipes/577982/
# The simple debug utility will help to detect unwonted changes to user's class
# during program execution. Just call it before and after some part of program
# and compare the results. Also, one can dump the contents of the class into
# files and investigate details. Any additional type of collection can be
# embodied with small helper functions.
import collections
from collections import Mapping, Set, Sequence,namedtuple
from collections import deque as deque
from datetime import datetime
from datetime import date as date
__string_types__ = (str, unicode) if str is bytes else (str, bytes)
__builtinTypes__ =['int', 'float','bool','date','str'] + [str(m)[8:-2] for m in __string_types__]
def deque_view(obj):
''' helper function for deque '''
de = enumerate(obj)
internal = list()
while True:
try:
item = next(de)
internal.append(item)
except StopIteration:
break
res = collections.OrderedDict(internal)
return res.items()
def class_or_tuple_view(obj):
''' helper function for classes and namedtuples'''
internaldict = dict()
if hasattr(obj,'_asdict'):
internaldict = obj._asdict()
else:
internaldict = { member:getattr(obj, member) for member in dir(obj) \
if not member.startswith('__') and not callable(getattr(obj, member)) }
keylist = list(internaldict.keys())
keylist.sort(key=str.upper)
res = collections.OrderedDict([(key,internaldict[key]) for key in keylist])
return res.items()
iteritems = lambda mapping: getattr(mapping, 'iteritems', mapping.items)()
def objview(obj,withValues,path = str()):
if not len(path): path = type(obj).__name__ + '.'
iterator = None
if isinstance(obj,deque):
iterator = deque_view
else:
if isinstance(obj, Mapping):
iterator = iteritems
else:
if isinstance(obj, (Sequence, Set)) and not isinstance(obj,__string_types__) \
and not hasattr(obj,'_asdict'):
if isinstance(obj,deque):
iterator = deque_view
else:
iterator = enumerate
elif not type(obj).__name__ in __builtinTypes__:
iterator = class_or_tuple_view
if iterator:
for path_component, value in iterator(obj):
valuetype = type(value).__name__
nextpath = path + ('[%s]' % str(path_component))
if (not withValues): yield nextpath, valuetype
for result in objview(value,withValues,nextpath):
if (withValues or (valuetype not in __builtinTypes__)): yield result
else:
yield path, obj
def objsignature(obj,fileName = None):
res = objview(obj,True)
r_str = '%s %s%s'
for x in res :
r_str = r_str %(x[0],str(x[1]), '\n%s %s%s')
r_str = r_str[:-7]
if (fileName):
debugFile = open(fileName,'w')
debugFile.write(r_str)
debugFile.close()
return hash(r_str)
if __name__ =='__main__':
myList = [1,2,3,[4,5,6]]
res = objview(myList,True)
for x in res :
print(x[0],' => ',x[1])
class MyRandomClass(object):
def __init__(self,limit):
super()
import random
self.numbers = [random.random() for i in range(limit)]
class MyClass(object):
def __init__(self,name):
super()
self.name = 'MyClass'
self.desription = (name,datetime.strptime('01/12/2012','%m/%d/%Y').date(),25)
self.myList = [1.0,2.0,3.0,[5.0,6.0,7.0]]
self.myDict = dict(a=1, b=2, c=3, d=set(['one','two','three']))
Temp = collections.namedtuple('namedtuple','first second third')
self.myNamedTuple = Temp('red','blue','green')
self.myTuple = (('white','black'),'yellow')
self.myFrosenSet = frozenset(['four','five','six'])
self.myDeque = deque()
self.myDeque.append(9.0)
self.myDeque.append(10.0)
self.myDeque.appendleft(8.0)
self.Randoms = MyRandomClass(5)
mycl = MyClass('The one')
print('Members of class are:')
res = objview(mycl,True)
for x in res :
print(x[0],' => ',x[1])
sig1 = objsignature(mycl,'before.txt')
print('class signature before',sig1)
mycl.myList[0] = 5.0
sig2 = objsignature(mycl,'after.txt')
print('class signature after',sig2)
if sig1 != sig2:
print('The content of class has been changed\n', \
'To investigate compare files before.txt and after.txt')
mycl.myList[0] = 1.0
sig3 = objsignature(mycl)
assert(sig1 == sig3)
Diff to Previous Revision
--- revision 1 2012-02-15 07:05:30
+++ revision 2 2012-02-15 17:39:09
@@ -38,7 +38,7 @@
internaldict = { member:getattr(obj, member) for member in dir(obj) \
if not member.startswith('__') and not callable(getattr(obj, member)) }
keylist = list(internaldict.keys())
- keylist.sort()
+ keylist.sort(key=str.upper)
res = collections.OrderedDict([(key,internaldict[key]) for key in keylist])
return res.items()
@@ -91,6 +91,12 @@
for x in res :
print(x[0],' => ',x[1])
+ class MyRandomClass(object):
+ def __init__(self,limit):
+ super()
+ import random
+ self.numbers = [random.random() for i in range(limit)]
+
class MyClass(object):
def __init__(self,name):
super()
@@ -106,8 +112,9 @@
self.myDeque.append(9.0)
self.myDeque.append(10.0)
self.myDeque.appendleft(8.0)
+ self.Randoms = MyRandomClass(5)
- mycl =MyClass('The one')
+ mycl = MyClass('The one')
print('Members of class are:')
res = objview(mycl,True)
for x in res :