traceing object usage: how to trace attribute access

Volker Apelt gq437x at yahoo.de
Wed Mar 5 15:11:54 CET 2003


I am looking for ways to trace all kind of access to objects
of some class with least impact on the traced class and 
no impact on the caller.

I have already found some code written by Greg Chapman 
(glchapman at earthlink.net) on google, which traces  
object method calls using __getattribute__. (see below)
With a small extension it traces all kinds 
of object attribute access, too.  

But it can not tell whether it is read or write 
access to  object attributes.
And it traces all access, even from inside the same 
object.   


How can I find out, if this call of __getattribute__ 
is a read or a write (assign) to some attribute?

  other = C()
  print other.x # read access
  other.x = 1   # write access 

How can I find out, if this call of __getattribute__ 
is from with in the same instance of this object, 
like in self.x ?
 
  self.x  # internal access
  other.x # external access 
  
Thank you,


Volker 


#
# code of  Greg Chapman with modifications 
# 
import sys, types 
class Traced(object):
    traceStream = sys.stdout
    callNo = long(1)
    def __trace_call__(self, fp, fmt, *args):
        if fp is None:
            fp = Traced.traceStream
        fp.write((fmt+'\n') % args)

    def __getattribute__(self, name):
        res = super(Traced, self).__getattribute__(name)
        if name == '__trace_call__' or name == '__class__':
            return res
        if isinstance(res, types.MethodType):
            if self.__class__.traceStream:
                fullname = self.__class__.__name__ + "." + name
                res = TracingWrapper(fullname, res, self)
        else:
            fullname = self.__class__.__name__ + "." + name
            self.__trace_call__(None, "ACCESS: %s %s",fullname, res )
        return res

class TracingWrapper(object):
    def __init__(self, name, func, inst):
        self.__name__ = name
        self.func = func
        self.inst = inst

    def __call__(self, *args, **kw):
        callNo =  Traced.callNo
        Traced.callNo += 1
        out = None
        self.inst.__trace_call__(out,
                                 "CALL: %05d inst=%s, %s(  args=%s, kw=%s)",
                                 callNo,self.inst, self.__name__, args, kw)
        try:
            rv = self.func(*args, **kw)
        except:
            t, v, tb = sys.exc_info()
            self.inst.__trace_call__(out,
                                     "EXCEPTION: %05d %s, exception= %s: %s",
                                     callNo,self.__name__, t, v)
            raise t, v, tb
        else:
            self.inst.__trace_call__(out,
                                     "RETURN: %05d %s  value=%s",
                                     callNo,self.__name__, rv)
            return rv


        
if __name__ == '__main__':
    #### TEST CASE

    class C(Traced):#{
        def __init__(self, x=0): self.x = x
        def m1(self, x): self.x = x
        def m2(self, y): 
            self.x += 1
            return self.x + y
        def raise_if2(self,x):
            if x == 2:
                raise ValueError('forced execption x == 2')
            return self.x 
    #}
    
    ##     
    c = C()
        
    c.m1(2)

    c.x = 2
    
    c.raise_if2(1)
    try:
        c.raise_if2(2)
    except Exception,e:
        print '**** EXCEPTION',e
        pass

    print c.x
    
    c.raise_if2(3)


-- 
Volker Apelt                   




More information about the Python-list mailing list