Welcome, guest | Sign In | My Account | Store | Cart
#  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 :

History