Logger.Logger

Package Logger :: Module Logger |
[hide private]
[frames] | no frames]

# Source Code for Module Logger.Logger

      1  # $Id: Logger.py 2 2006-05-06 22:54:39Z glandrum $
      2  #----------------------------------------------------------------------
      3  # Name:        Logger.py
      4  # Purpose:     Provides a Logger class which can be wrapped around another
      5  #              python class to log method calls and attribute changes.
      6  # Requires:    Python 2.0 (or higher?)
      7  #
      8  # Author:      greg Landrum (Landrum@RationalDiscovery.com)
      9  # License:
     10  #
     11  #       Copyright (c) 2001-2006 Greg Landrum and Rational Discovery LLC,
     12  #
     13  #   @@ All Rights Reserved  @@
     14  #
     15  #----------------------------------------------------------------------
     16  """ Provides a Logger class which can be wrapped around another
     17               python class to log method calls and attribute changes.
     18  """
     19
     20  import types
     21  import re
     22
     23  reType = type(re.compile('f*'))
     24  stringTypes = [types.StringType,types.UnicodeType,reType]
     25
     26 -def isPresent(what,checkList):
     27    """ checks to see if any of the regular expressions in a list match a string
     28
     29     **Arguments**
     30
     31       - what: the thing to match against
     32
     33       - checkList: the list of regexps to try the match with
     34
     35     **Returns**
     36
     37       1 or 0 depending upon whether or not _what_ was found in checkList
     38
     39     **Notes**
     40
     41       - the search is done using _match_, so we match *any* part of _what_
     42
     43    """
     44    for entry in checkList:
     45      if type(entry) == reType:
     46        if entry.match(what) is not None:
     47          return 1
     48      else:
     49        if what == entry:
     50          return 1
     51    return 0
     52
     53 -class Callable(object):
     54    """ this is just a simple class with a __call__ method we'll use to pass
     55        any method invocations back to the caller.
     56
     57    """
     58 -  def __init__(self,log,obj,method):
     59      """ Constructor
     60
     61        **Arguments:**
     62
     63          - log: a python list (or anything else supporting the append method)
     64               which is used to log the actual invocation of a method
     65
     66          - obj: the object which is calling the method
     67
     68          - method: the *name* of the method to be invoked
     69
     70      """
     71      self._obj = obj
     72      self._log = log
     73      self._method = method
     74 -  def __call__(self,*args,**kwargs):
     75      """ logs the method name and arguments and makes the call
     76
     77      """
     78      self._log.append((self._method,args,kwargs))
     79      return getattr(self._obj,self._method)(*args,**kwargs)
     80
     81 -class Logger(object):
     82    """ This is the actual wrapper class.
     83
     84      The wrapper is fairly thin; it only has one methods of its own:
     85
     86        - _LoggerGetLog()
     87
     88      and then several instance variables:
     89
     90        - _loggerFlushCommand
     91
     92        - _loggerClass
     93
     94        - _loggerObj
     95
     96        - _loggerCommandLog
     97
     98        - _loggerIgnore
     99
    100      These names were chosen to minimize the likelihood of a collision
    101       with the attributes of a wrapped class.  Obviously... ;-)
    102
    103      The general idea of using this is that you wrap a class in the logger,
    104        and then use the class as you normally would.  Whenever you want to
    105        get the contents of the log (for example after running your program for
    106        a while), you can call _loggerCommandLog.  The resulting list can be
    107        played back in another (or the same) object using the replay() function
    108        defined below.
    109
    110      The logger can, optionally, be set to flush its log whenever a method with
    111        a particular name is invoked.  For example, you may want to be wrapping
    112        some kind of drawing canvas and want to reset the log whenever the canvas
    113        is cleared because there's no point in storing commands which will have
    114        no effect on the final drawing.
    115
    116      **Note**
    117
    118        because of the way I've worked this, the log will actually be flushed
    119        whenever the client program accesses the flush method, it need not be invoked.
    120        i.e. if the loggerFlushCommand is 'foo', then doing either wrappedObj.foo() or
    121        wrappedObj.foo will reset the log.  This is undesirable and will most likely
    122        be fixed in a future version
    123
    124    """
    125 -  def __init__(self,klass,*args,**kwargs):
    126      """  Constructor
    127
    128        **Arguments**
    129
    130          The one required argument here is _klass_, which is the class
    131          to be wrapped.
    132
    133        **Optional Keyword Arguments**
    134
    135          - loggerFlushCommand: the name of the attribute which will flush the log
    136
    137          - loggerIgnore: a list of regexps defining methods which should not be
    138            logged
    139
    140        **All other arguments are passed to the constructor for _klass_ **
    141
    142      """
    143      if kwargs.has_key('loggerFlushCommand'):
    144        self.__dict__['_loggerFlushCommand'] = kwargs['loggerFlushCommand']
    145        del kwargs['loggerFlushCommand']
    146      else:
    147        self.__dict__['_loggerFlushCommand'] = None
    148      if kwargs.has_key('loggerIgnore'):
    149        tempL = kwargs['loggerIgnore']
    150        for entry in tempL:
    151          if type(entry) not in stringTypes:
    152            raise ValueError,'All entries in loggerIgnore must be either strings or regexps'
    153        self.__dict__['_loggerIgnore'] = tempL
    154        del kwargs['loggerIgnore']
    155      else:
    156        self.__dict__['_loggerIgnore'] = []
    157      self.__dict__['_loggerClass'] = klass
    158      self.__dict__['_loggerObj'] = klass(*args,**kwargs)
    159      self.__dict__['_loggerCommandLog'] = []
    160
    161 -  def _LoggerGetLog(self):
    162      """ Returns the contents of the command log as a python list
    163
    164      """
    165      return self._loggerCommandLog
    166
    167 -  def __getattr__(self,which):
    168      """ here's where the logging of method invocations takes place
    169
    170      """
    171      if hasattr(self._loggerObj,which):
    172        tmpAttr = getattr(self._loggerObj,which)
    173        if type(tmpAttr) == types.MethodType:
    174          loggerFlushCommand = self._loggerFlushCommand
    175          if which == loggerFlushCommand:
    176            self._loggerCommandLog = []
    177            return Callable([],self._loggerObj,which)
    178          elif self._loggerIgnore != [] and isPresent(which,self._loggerIgnore):
    179            return Callable([],self._loggerObj,which)
    180          else:
    181            return Callable(self._loggerCommandLog,self._loggerObj,which)
    182        else:
    183          return tmpAttr
    184      else:
    185        raise AttributeError, '%s instance has no attribute %s'%(repr(self._loggerClass.__name__),repr(which))
    186
    187 -  def __setattr__(self,which,val):
    188      """ setattr calls (i.e. wrappedObject.foo = 1) are also logged
    189
    190      """
    191      d = self.__dict__
    192      if d.has_key(which):
    193        d[which] = val
    194      else:
    195        self._loggerCommandLog.append((setattr,which,val))
    196        setattr(self._loggerObj,which,val)
    197        return val
    198
    199 -def replay(logItems,obj):
    200    """ loops through the items in a Logger log list and invokes
    201       them in obj
    202
    203      **Arguments**
    204
    205        - logItems: a list of 3 tuples containing:
    206
    207           1) method name
    208
    209           2) tuple of positional arguments
    210
    211           3) dictionary of keyword arguments
    212
    213        - obj: the object in which the log should be replayed
    214
    215      **Returns**
    216
    217        a list with the the return values of all the method
    218        invocations/attribute assignments
    219
    220    """
    221    if isinstance(logItems,Logger):
    222      logItems = logItems._LoggerGetLog()
    223    resList = []
    224    for method,a1,a2 in logItems:
    225      if callable(method):
    226        if method == setattr:
    227          method(obj,a1,a2)
    228          resList.append(a2)
    229        else:
    230          resList.append(method(obj,a1,a2))
    231      else:
    232        a = getattr(obj,method)
    233        resList.append(apply(a,a1,a2))
    234    return resList
    235