2008-04-01
A simple Python class to trace access to object attributes
Every now and then I run into a situation where I want to know what attributes on an object are getting accessed when. Fortunately it's pretty easy to knock together something to do this, in a variant on the mutating proxy pattern.
Here is a sample implementation, as a Tracer class:
def _repon(obj, n):
return (not obj._t_il) or \
(n in obj._t_il)
class Tracer(object):
def __init__(self, real,
name = None, monitor = None):
self._t_ro = real
if name:
self._t_nm = name
else:
cl = real.__class__
self._t_nm = "<%s.%s>" % \
(cl.__module__, cl.__name__)
self._t_il = monitor
def __getattr__(self, n):
if _repon(self, n):
print "get %s.%s" % (self._t_nm, n)
return getattr(self._t_ro, n)
def __setattr__(self, n, v):
if n in ("_t_nm", "_t_ro", "_t_il"):
super(Tracer, self).__setattr__(n, v)
return
if _repon(self, n):
print "set %s.%s" % (self._t_nm, n)
setattr(self._t_ro, n, v)
def __delattr__(self, n):
if _repon(self, n):
print "del %s.%s" % (self._t_nm, n)
delattr(self._t_ro, n)
You use it by replacing the original object with Tracer(obj). The
optional name argument will be used as the object's name in output;
the optional monitor argument is a list of attributes to monitor
access to (if omitted, all attributes are monitored).
There's a couple of limitations in this approach:
- 'get' is printed when you do
hasattr(), becausehasattr()works by getting the attribute and seeing if that throws an error. - you can't tell a method call from an attribute access, because Python doesn't really distinguish them.
As a side note, the _repon function is not a class function in order
to keep it out of the object namespace. I could keep the three variables
out too if I wanted to work hard enough, by using a closure, but that
would complicate and obscure the code.
(Disclaimer: the indentation has been shrunk and the variable names shortened in the interests of not overflowing WanderingThoughts' margins. I do not write real code that is this condensed.)