-# Written by Brendan O'Connor, brenocon@gmail.com, www.anyall.org\r
-# * Originally written Aug. 2005\r
-# * Posted to gist.github.com/16173 on Oct. 2008\r
-\r
-# Copyright (c) 2003-2006 Open Source Applications Foundation\r
-#\r
-# Licensed under the Apache License, Version 2.0 (the "License");\r
-# you may not use this file except in compliance with the License.\r
-# You may obtain a copy of the License at\r
-#\r
-# http://www.apache.org/licenses/LICENSE-2.0\r
-#\r
-# Unless required by applicable law or agreed to in writing, software\r
-# distributed under the License is distributed on an "AS IS" BASIS,\r
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-# See the License for the specific language governing permissions and\r
-# limitations under the License.\r
-\r
-import re, sys, types\r
-\r
-"""\r
-Have all your function & method calls automatically logged, in indented outline\r
-form - unlike the stack snapshots in an interactive debugger, it tracks call\r
-structure & stack depths across time!\r
-\r
-It hooks into all function calls that you specify, and logs each time they're\r
-called. I find it especially useful when I don't know what's getting called\r
-when, or need to continuously test for state changes. (by hacking this file)\r
-\r
-Originally inspired from the python cookbook: \r
-http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/198078\r
-\r
-Currently you can\r
- - tag functions or individual methods to be autologged\r
- - tag an entire class's methods to be autologged\r
- - tag an entire module's classes and functions to be autologged\r
-\r
-TODO:\r
- - allow tagging of ALL modules in the program on startup?\r
-\r
-CAVEATS:\r
- - certain classes barf when you logclass() them -- most notably,\r
- SWIG-generated wrappers, and perhaps others.\r
-\r
-USAGE: see examples on the bottom of this file.\r
-\r
-\r
-Viewing tips\r
-============\r
-\r
-If your terminal can't keep up, try xterm or putty, they seem to be highest\r
-performance. xterm is available for all platforms through X11...\r
-\r
-Also try: (RunChandler > log &); tail -f log\r
-\r
-Also, you can "less -R log" afterward and get the colors correct.\r
-\r
-If you have long lines, less -RS kills wrapping, enhancing readability. Also\r
-can chop at formatAllArgs().\r
-\r
-If you want long lines to be chopped realtime, try piping through less::\r
-\r
- RunChandler | less -RS\r
-\r
-but then you have to hit 'space' lots to prevent chandler from freezing.\r
-less's 'F' command is supposed to do this correctly but doesn't work for me.\r
-"""\r
-\r
-\r
-#@@@ should use the standard python logging system?\r
-log = sys.stdout\r
-\r
-# Globally incremented across function calls, so tracks stack depth\r
-indent = 0\r
-indStr = ' '\r
-\r
-\r
-# ANSI escape codes for terminals.\r
-# X11 xterm: always works, all platforms\r
-# cygwin dosbox: run through |cat and then colors work\r
-# linux: works on console & gnome-terminal\r
-# mac: untested\r
-\r
-\r
-BLACK = "\033[0;30m"\r
-BLUE = "\033[0;34m"\r
-GREEN = "\033[0;32m"\r
-CYAN = "\033[0;36m"\r
-RED = "\033[0;31m"\r
-PURPLE = "\033[0;35m"\r
-BROWN = "\033[0;33m"\r
-GRAY = "\033[0;37m"\r
-BOLDGRAY = "\033[1;30m"\r
-BOLDBLUE = "\033[1;34m"\r
-BOLDGREEN = "\033[1;32m"\r
-BOLDCYAN = "\033[1;36m"\r
-BOLDRED = "\033[1;31m"\r
-BOLDPURPLE = "\033[1;35m"\r
-BOLDYELLOW = "\033[1;33m"\r
-WHITE = "\033[1;37m"\r
-\r
-NORMAL = "\033[0m"\r
-\r
-\r
-def indentlog(message):\r
- global log, indStr, indent\r
- print >>log, "%s%s" %(indStr*indent, message)\r
- log.flush()\r
-\r
-def shortstr(obj):\r
- """\r
- Where to put gritty heuristics to make an object appear in most useful\r
- form. defaults to __str__.\r
- """\r
- if "wx." in str(obj.__class__) or obj.__class__.__name__.startswith("wx"):\r
- shortclassname = obj.__class__.__name__\r
- ##shortclassname = str(obj.__class__).split('.')[-1]\r
- if hasattr(obj, "blockItem") and hasattr(obj.blockItem, "blockName"):\r
- moreInfo = "block:'%s'" %obj.blockItem.blockName\r
- else:\r
- moreInfo = "at %d" %id(obj)\r
- return "<%s %s>" % (shortclassname, moreInfo)\r
- else:\r
- return str(obj)\r
-\r
-def formatAllArgs(args, kwds):\r
- """\r
- makes a nice string representation of all the arguments\r
- """\r
- allargs = []\r
- for item in args:\r
- allargs.append('%s' % shortstr(item))\r
- for key,item in kwds.items():\r
- allargs.append('%s=%s' % (key,shortstr(item)))\r
- formattedArgs = ', '.join(allargs)\r
- if len(formattedArgs) > 150:\r
- return formattedArgs[:146] + " ..."\r
- return formattedArgs\r
-\r
-\r
-def logmodules(listOfModules):\r
- for m in listOfModules: \r
- bindmodule(m)\r
-\r
-def logmodule(module, logMatch=".*", logNotMatch="nomatchasfdasdf"):\r
- """ \r
- WARNING: this seems to break if you import SWIG wrapper classes\r
- directly into the module namespace ... logclass() creates weirdness when\r
- used on them, for some reason.\r
- \r
- @param module: could be either an actual module object, or the string\r
- you can import (which seems to be the same thing as its\r
- __name__). So you can say logmodule(__name__) at the end\r
- of a module definition, to log all of it.\r
- """\r
- \r
- allow = lambda s: re.match(logMatch, s) and not re.match(logNotMatch, s)\r
- \r
- if isinstance(module, str):\r
- d = {}\r
- exec "import %s" % module in d\r
- import sys\r
- module = sys.modules[module]\r
- \r
- names = module.__dict__.keys()\r
- for name in names:\r
- if not allow(name): continue\r
- \r
- value = getattr(module, name)\r
- if isinstance(value, type):\r
- setattr(module, name, logclass(value))\r
- print>>log,"autolog.logmodule(): bound %s" %name\r
- elif isinstance(value, types.FunctionType):\r
- setattr(module, name, logfunction(value))\r
- print>>log,"autolog.logmodule(): bound %s" %name\r
-\r
-def logfunction(theFunction, displayName=None):\r
- """a decorator."""\r
- if not displayName: displayName = theFunction.__name__\r
-\r
- def _wrapper(*args, **kwds):\r
- global indent\r
- argstr = formatAllArgs(args, kwds)\r
- \r
- # Log the entry into the function\r
- indentlog("%s%s%s (%s) " % (BOLDRED,displayName,NORMAL, argstr))\r
- log.flush()\r
- \r
- indent += 1\r
- returnval = theFunction(*args,**kwds)\r
- indent -= 1\r
- \r
- # Log return\r
- ##indentlog("return: %s"% shortstr(returnval)\r
- return returnval\r
- return _wrapper\r
-\r
-def logmethod(theMethod, displayName=None):\r
- """use this for class or instance methods, it formats with the object out front."""\r
- if not displayName: displayName = theMethod.__name__\r
- def _methodWrapper(self, *args, **kwds):\r
- "Use this one for instance or class methods"\r
- global indent\r
-\r
- argstr = formatAllArgs(args, kwds) \r
- selfstr = shortstr(self)\r
- \r
- #print >> log,"%s%s. %s (%s) " % (indStr*indent,selfstr,methodname,argstr)\r
- indentlog("%s.%s%s%s (%s) " % (selfstr, BOLDRED,theMethod.__name__,NORMAL, argstr))\r
- log.flush()\r
- \r
- indent += 1\r
- \r
- if theMethod.__name__ == 'OnSize':\r
- indentlog("position, size = %s%s %s%s" %(BOLDBLUE, self.GetPosition(), self.GetSize(), NORMAL))\r
-\r
- returnval = theMethod(self, *args,**kwds)\r
-\r
- indent -= 1\r
- \r
- return returnval\r
- return _methodWrapper\r
-\r
-\r
-def logclass(cls, methodsAsFunctions=False, \r
- logMatch=".*", logNotMatch="asdfnomatch"):\r
- """ \r
- A class "decorator". But python doesn't support decorator syntax for\r
- classes, so do it manually::\r
- \r
- class C(object):\r
- ...\r
- C = logclass(C)\r
- \r
- @param methodsAsFunctions: set to True if you always want methodname first\r
- in the display. Probably breaks if you're using class/staticmethods?\r
- """\r
-\r
- allow = lambda s: re.match(logMatch, s) and not re.match(logNotMatch, s) and \\r
- s not in ('__str__','__repr__')\r
- \r
- namesToCheck = cls.__dict__.keys()\r
- \r
- for name in namesToCheck:\r
- if not allow(name): continue\r
- # unbound methods show up as mere functions in the values of\r
- # cls.__dict__,so we have to go through getattr\r
- value = getattr(cls, name)\r
- \r
- if methodsAsFunctions and callable(value):\r
- setattr(cls, name, logfunction(value)) \r
- elif isinstance(value, types.MethodType):\r
- #a normal instance method\r
- if value.im_self == None: \r
- setattr(cls, name, logmethod(value))\r
- \r
- #class & static method are more complex.\r
- #a class method\r
- elif value.im_self == cls: \r
- w = logmethod(value.im_func, \r
- displayName="%s.%s" %(cls.__name__, value.__name__))\r
- setattr(cls, name, classmethod(w))\r
- else: assert False\r
-\r
- #a static method\r
- elif isinstance(value, types.FunctionType):\r
- w = logfunction(value, \r
- displayName="%s.%s" %(cls.__name__, value.__name__))\r
- setattr(cls, name, staticmethod(w))\r
- return cls\r
-\r
-class LogMetaClass(type):\r
- """\r
- Alternative to logclass(), you set this as a class's __metaclass__.\r
- \r
- It will not work if the metaclass has already been overridden (e.g.\r
- schema.Item or zope.interface (used in Twisted) \r
- \r
- Also, it should fail for class/staticmethods, that hasnt been added here\r
- yet. \r
- """\r
- \r
- def __new__(cls,classname,bases,classdict):\r
- logmatch = re.compile(classdict.get('logMatch','.*'))\r
- lognotmatch = re.compile(classdict.get('logNotMatch', 'nevermatchthisstringasdfasdf'))\r
- \r
- for attr,item in classdict.items():\r
- if callable(item) and logmatch.match(attr) and not lognotmatch.match(attr):\r
- classdict['_H_%s'%attr] = item # rebind the method\r
- classdict[attr] = logmethod(item) # replace method by wrapper\r
-\r
- return type.__new__(cls,classname,bases,classdict) \r
-\r
-\r
- \r
-# ---------------------------- Tests and examples ----------------------------\r
-\r
-if __name__=='__main__':\r
- print; print "------------------- single function logging ---------------"\r
- @logfunction\r
- def test():\r
- return 42\r
- \r
- test()\r
-\r
- print; print "------------------- single method logging -----------------"\r
- class Test1(object):\r
- def __init__(self):\r
- self.a = 10\r
- \r
- @logmethod\r
- def add(self,a,b): return a+b\r
- \r
- @logmethod\r
- def fac(self,val):\r
- if val == 1:\r
- return 1\r
- else:\r
- return val * self.fac(val-1)\r
- \r
- @logfunction\r
- def fac2(self, val):\r
- if val == 1:\r
- return 1\r
- else:\r
- return val * self.fac2(val-1) \r
- \r
- t = Test1()\r
- t.add(5,6)\r
- t.fac(4)\r
- print "--- tagged as @logfunction, doesn't understand 'self' is special:"\r
- t.fac2(4)\r
- \r
-\r
- print; print """-------------------- class "decorator" usage ------------------"""\r
- class Test2(object):\r
- #will be ignored\r
- def __init__(self):\r
- self.a = 10\r
- def ignoreThis(self): pass\r
- \r
-\r
- def add(self,a,b):return a+b\r
- def fac(self,val):\r
- if val == 1:\r
- return 1\r
- else:\r
- return val * self.fac(val-1)\r
- \r
- Test2 = logclass(Test2, logMatch='fac|add')\r
-\r
- t2 = Test2()\r
- t2.add(5,6)\r
- t2.fac(4)\r
- t2.ignoreThis()\r
- \r
- \r
- print; print "-------------------- metaclass usage ------------------"\r
- class Test3(object):\r
- __metaclass__ = LogMetaClass\r
- logNotMatch = 'ignoreThis'\r
- \r
- def __init__(self): pass\r
- \r
- def fac(self,val):\r
- if val == 1:\r
- return 1\r
- else:\r
- return val * self.fac(val-1)\r
- def ignoreThis(self): pass\r
- t3 = Test3()\r
- t3.fac(4)\r
- t3.ignoreThis()\r
-\r
- print; print "-------------- testing static & classmethods --------------"\r
- class Test4(object):\r
- @classmethod\r
- def cm(cls, a, b): \r
- print cls\r
- return a+b\r
-\r
- def im(self, a, b): \r
- print self\r
- return a+b\r
- \r
- @staticmethod\r
- def sm(a,b): return a+b\r
-\r
- Test4 = logclass(Test4)\r
-\r
- Test4.cm(4,3)\r
- Test4.sm(4,3)\r
-\r
- t4 = Test4()\r
- t4.im(4,3)\r
- t4.sm(4,3)\r
- t4.cm(4,3)\r
- \r
- #print; print "-------------- static & classmethods: where to put decorators? --------------"\r
- #class Test5(object):\r
- #@classmethod\r
- #@logmethod\r
- #def cm(cls, a, b): \r
- #print cls\r
- #return a+b\r
- #@logmethod\r
- #def im(self, a, b): \r
- #print self\r
- #return a+b\r
- \r
- #@staticmethod\r
- #@logfunction\r
- #def sm(a,b): return a+b\r
-\r
-\r
- #Test5.cm(4,3)\r
- #Test5.sm(4,3)\r
-\r
- #t5 = Test5()\r
- #t5.im(4,3)\r
- #t5.sm(4,3)\r
- #t5.cm(4,3) \r
+# Written by Brendan O'Connor, brenocon@gmail.com, www.anyall.org
+# * Originally written Aug. 2005
+# * Posted to gist.github.com/16173 on Oct. 2008
+
+# Copyright (c) 2003-2006 Open Source Applications Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re, sys, types
+
+"""
+Have all your function & method calls automatically logged, in indented outline
+form - unlike the stack snapshots in an interactive debugger, it tracks call
+structure & stack depths across time!
+
+It hooks into all function calls that you specify, and logs each time they're
+called. I find it especially useful when I don't know what's getting called
+when, or need to continuously test for state changes. (by hacking this file)
+
+Originally inspired from the python cookbook:
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/198078
+
+Currently you can
+ - tag functions or individual methods to be autologged
+ - tag an entire class's methods to be autologged
+ - tag an entire module's classes and functions to be autologged
+
+TODO:
+ - allow tagging of ALL modules in the program on startup?
+
+CAVEATS:
+ - certain classes barf when you logclass() them -- most notably,
+ SWIG-generated wrappers, and perhaps others.
+
+USAGE: see examples on the bottom of this file.
+
+
+Viewing tips
+============
+
+If your terminal can't keep up, try xterm or putty, they seem to be highest
+performance. xterm is available for all platforms through X11...
+
+Also try: (RunChandler > log &); tail -f log
+
+Also, you can "less -R log" afterward and get the colors correct.
+
+If you have long lines, less -RS kills wrapping, enhancing readability. Also
+can chop at formatAllArgs().
+
+If you want long lines to be chopped realtime, try piping through less::
+
+ RunChandler | less -RS
+
+but then you have to hit 'space' lots to prevent chandler from freezing.
+less's 'F' command is supposed to do this correctly but doesn't work for me.
+"""
+
+
+#@@@ should use the standard python logging system?
+log = sys.stdout
+
+# Globally incremented across function calls, so tracks stack depth
+indent = 0
+indStr = ' '
+
+
+# ANSI escape codes for terminals.
+# X11 xterm: always works, all platforms
+# cygwin dosbox: run through |cat and then colors work
+# linux: works on console & gnome-terminal
+# mac: untested
+
+
+BLACK = "\033[0;30m"
+BLUE = "\033[0;34m"
+GREEN = "\033[0;32m"
+CYAN = "\033[0;36m"
+RED = "\033[0;31m"
+PURPLE = "\033[0;35m"
+BROWN = "\033[0;33m"
+GRAY = "\033[0;37m"
+BOLDGRAY = "\033[1;30m"
+BOLDBLUE = "\033[1;34m"
+BOLDGREEN = "\033[1;32m"
+BOLDCYAN = "\033[1;36m"
+BOLDRED = "\033[1;31m"
+BOLDPURPLE = "\033[1;35m"
+BOLDYELLOW = "\033[1;33m"
+WHITE = "\033[1;37m"
+
+NORMAL = "\033[0m"
+
+
+def indentlog(message):
+ global log, indStr, indent
+ print >>log, "%s%s" %(indStr*indent, message)
+ log.flush()
+
+def shortstr(obj):
+ """
+ Where to put gritty heuristics to make an object appear in most useful
+ form. defaults to __str__.
+ """
+ if "wx." in str(obj.__class__) or obj.__class__.__name__.startswith("wx"):
+ shortclassname = obj.__class__.__name__
+ ##shortclassname = str(obj.__class__).split('.')[-1]
+ if hasattr(obj, "blockItem") and hasattr(obj.blockItem, "blockName"):
+ moreInfo = "block:'%s'" %obj.blockItem.blockName
+ else:
+ moreInfo = "at %d" %id(obj)
+ return "<%s %s>" % (shortclassname, moreInfo)
+ else:
+ return str(obj)
+
+def formatAllArgs(args, kwds):
+ """
+ makes a nice string representation of all the arguments
+ """
+ allargs = []
+ for item in args:
+ allargs.append('%s' % shortstr(item))
+ for key,item in kwds.items():
+ allargs.append('%s=%s' % (key,shortstr(item)))
+ formattedArgs = ', '.join(allargs)
+ if len(formattedArgs) > 150:
+ return formattedArgs[:146] + " ..."
+ return formattedArgs
+
+
+def logmodules(listOfModules):
+ for m in listOfModules:
+ bindmodule(m)
+
+def logmodule(module, logMatch=".*", logNotMatch="nomatchasfdasdf"):
+ """
+ WARNING: this seems to break if you import SWIG wrapper classes
+ directly into the module namespace ... logclass() creates weirdness when
+ used on them, for some reason.
+
+ @param module: could be either an actual module object, or the string
+ you can import (which seems to be the same thing as its
+ __name__). So you can say logmodule(__name__) at the end
+ of a module definition, to log all of it.
+ """
+
+ allow = lambda s: re.match(logMatch, s) and not re.match(logNotMatch, s)
+
+ if isinstance(module, str):
+ d = {}
+ exec "import %s" % module in d
+ import sys
+ module = sys.modules[module]
+
+ names = module.__dict__.keys()
+ for name in names:
+ if not allow(name): continue
+
+ value = getattr(module, name)
+ if isinstance(value, type):
+ setattr(module, name, logclass(value))
+ print>>log,"autolog.logmodule(): bound %s" %name
+ elif isinstance(value, types.FunctionType):
+ setattr(module, name, logfunction(value))
+ print>>log,"autolog.logmodule(): bound %s" %name
+
+def logfunction(theFunction, displayName=None):
+ """a decorator."""
+ if not displayName: displayName = theFunction.__name__
+
+ def _wrapper(*args, **kwds):
+ global indent
+ argstr = formatAllArgs(args, kwds)
+
+ # Log the entry into the function
+ indentlog("%s%s%s (%s) " % (BOLDRED,displayName,NORMAL, argstr))
+ log.flush()
+
+ indent += 1
+ returnval = theFunction(*args,**kwds)
+ indent -= 1
+
+ # Log return
+ ##indentlog("return: %s"% shortstr(returnval)
+ return returnval
+ return _wrapper
+
+def logmethod(theMethod, displayName=None):
+ """use this for class or instance methods, it formats with the object out front."""
+ if not displayName: displayName = theMethod.__name__
+ def _methodWrapper(self, *args, **kwds):
+ "Use this one for instance or class methods"
+ global indent
+
+ argstr = formatAllArgs(args, kwds)
+ selfstr = shortstr(self)
+
+ #print >> log,"%s%s. %s (%s) " % (indStr*indent,selfstr,methodname,argstr)
+ indentlog("%s.%s%s%s (%s) " % (selfstr, BOLDRED,theMethod.__name__,NORMAL, argstr))
+ log.flush()
+
+ indent += 1
+
+ if theMethod.__name__ == 'OnSize':
+ indentlog("position, size = %s%s %s%s" %(BOLDBLUE, self.GetPosition(), self.GetSize(), NORMAL))
+
+ returnval = theMethod(self, *args,**kwds)
+
+ indent -= 1
+
+ return returnval
+ return _methodWrapper
+
+
+def logclass(cls, methodsAsFunctions=False,
+ logMatch=".*", logNotMatch="asdfnomatch"):
+ """
+ A class "decorator". But python doesn't support decorator syntax for
+ classes, so do it manually::
+
+ class C(object):
+ ...
+ C = logclass(C)
+
+ @param methodsAsFunctions: set to True if you always want methodname first
+ in the display. Probably breaks if you're using class/staticmethods?
+ """
+
+ allow = lambda s: re.match(logMatch, s) and not re.match(logNotMatch, s) and \
+ s not in ('__str__','__repr__')
+
+ namesToCheck = cls.__dict__.keys()
+
+ for name in namesToCheck:
+ if not allow(name): continue
+ # unbound methods show up as mere functions in the values of
+ # cls.__dict__,so we have to go through getattr
+ value = getattr(cls, name)
+
+ if methodsAsFunctions and callable(value):
+ setattr(cls, name, logfunction(value))
+ elif isinstance(value, types.MethodType):
+ #a normal instance method
+ if value.im_self == None:
+ setattr(cls, name, logmethod(value))
+
+ #class & static method are more complex.
+ #a class method
+ elif value.im_self == cls:
+ w = logmethod(value.im_func,
+ displayName="%s.%s" %(cls.__name__, value.__name__))
+ setattr(cls, name, classmethod(w))
+ else: assert False
+
+ #a static method
+ elif isinstance(value, types.FunctionType):
+ w = logfunction(value,
+ displayName="%s.%s" %(cls.__name__, value.__name__))
+ setattr(cls, name, staticmethod(w))
+ return cls
+
+class LogMetaClass(type):
+ """
+ Alternative to logclass(), you set this as a class's __metaclass__.
+
+ It will not work if the metaclass has already been overridden (e.g.
+ schema.Item or zope.interface (used in Twisted)
+
+ Also, it should fail for class/staticmethods, that hasnt been added here
+ yet.
+ """
+
+ def __new__(cls,classname,bases,classdict):
+ logmatch = re.compile(classdict.get('logMatch','.*'))
+ lognotmatch = re.compile(classdict.get('logNotMatch', 'nevermatchthisstringasdfasdf'))
+
+ for attr,item in classdict.items():
+ if callable(item) and logmatch.match(attr) and not lognotmatch.match(attr):
+ classdict['_H_%s'%attr] = item # rebind the method
+ classdict[attr] = logmethod(item) # replace method by wrapper
+
+ return type.__new__(cls,classname,bases,classdict)
+
+
+
+# ---------------------------- Tests and examples ----------------------------
+
+if __name__=='__main__':
+ print; print "------------------- single function logging ---------------"
+ @logfunction
+ def test():
+ return 42
+
+ test()
+
+ print; print "------------------- single method logging -----------------"
+ class Test1(object):
+ def __init__(self):
+ self.a = 10
+
+ @logmethod
+ def add(self,a,b): return a+b
+
+ @logmethod
+ def fac(self,val):
+ if val == 1:
+ return 1
+ else:
+ return val * self.fac(val-1)
+
+ @logfunction
+ def fac2(self, val):
+ if val == 1:
+ return 1
+ else:
+ return val * self.fac2(val-1)
+
+ t = Test1()
+ t.add(5,6)
+ t.fac(4)
+ print "--- tagged as @logfunction, doesn't understand 'self' is special:"
+ t.fac2(4)
+
+
+ print; print """-------------------- class "decorator" usage ------------------"""
+ class Test2(object):
+ #will be ignored
+ def __init__(self):
+ self.a = 10
+ def ignoreThis(self): pass
+
+
+ def add(self,a,b):return a+b
+ def fac(self,val):
+ if val == 1:
+ return 1
+ else:
+ return val * self.fac(val-1)
+
+ Test2 = logclass(Test2, logMatch='fac|add')
+
+ t2 = Test2()
+ t2.add(5,6)
+ t2.fac(4)
+ t2.ignoreThis()
+
+
+ print; print "-------------------- metaclass usage ------------------"
+ class Test3(object):
+ __metaclass__ = LogMetaClass
+ logNotMatch = 'ignoreThis'
+
+ def __init__(self): pass
+
+ def fac(self,val):
+ if val == 1:
+ return 1
+ else:
+ return val * self.fac(val-1)
+ def ignoreThis(self): pass
+ t3 = Test3()
+ t3.fac(4)
+ t3.ignoreThis()
+
+ print; print "-------------- testing static & classmethods --------------"
+ class Test4(object):
+ @classmethod
+ def cm(cls, a, b):
+ print cls
+ return a+b
+
+ def im(self, a, b):
+ print self
+ return a+b
+
+ @staticmethod
+ def sm(a,b): return a+b
+
+ Test4 = logclass(Test4)
+
+ Test4.cm(4,3)
+ Test4.sm(4,3)
+
+ t4 = Test4()
+ t4.im(4,3)
+ t4.sm(4,3)
+ t4.cm(4,3)
+
+ #print; print "-------------- static & classmethods: where to put decorators? --------------"
+ #class Test5(object):
+ #@classmethod
+ #@logmethod
+ #def cm(cls, a, b):
+ #print cls
+ #return a+b
+ #@logmethod
+ #def im(self, a, b):
+ #print self
+ #return a+b
+
+ #@staticmethod
+ #@logfunction
+ #def sm(a,b): return a+b
+
+
+ #Test5.cm(4,3)
+ #Test5.sm(4,3)
+
+ #t5 = Test5()
+ #t5.im(4,3)
+ #t5.sm(4,3)
+ #t5.cm(4,3)