# 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() 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 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) 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)