A simple Python class to trace access to object attributes

April 1, 2008

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
      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)
    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(), because hasattr() 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.)

Written on 01 April 2008.
« Keeping secrets as a system administrator
ZFS: Reservations versus quotas »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Tue Apr 1 22:33:28 2008
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.