dos2unix'ed for ^M at EOL
authorThierry Parmentelat <thierry.parmentelat@inria.fr>
Fri, 27 Sep 2013 17:29:50 +0000 (19:29 +0200)
committerThierry Parmentelat <thierry.parmentelat@inria.fr>
Fri, 27 Sep 2013 17:29:50 +0000 (19:29 +0200)
manifold/util/autolog.py

index 70c5c05..e88b6f3 100644 (file)
-# 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)