Welcome, guest | Sign In | My Account | Store | Cart
from inspect import ismethod
from threading import local

class __current_class(local):
    """current.info is a tuple that, in each thread, contains information about the method that is currently executing and the class in which its definition 

is.

    The tuple is (cls,self,methodname,args,kwargs), where:
    cls is the class in which the method's definition is;
    self is the cls instance passed to the method as its first argument;
    methodname is the name of the method;
    args is the argument tuple passed to the method;
    kwargs is a dictionary of keyword arguments passed to the method.
    """
    def __init__(self):
        local.__init__(self)
        self.info = (None,None,"",(),{})

current = __current_class() # see __current_class doc

class auto(type):
    """auto instances are classes that can mantain information about the method that is currently executing in a certain thread."""
    def __init__(cls,name,bases,dct):
        # returns a function that will replace 'method' so that its execution will update current.info, and then the original method will be called (and its 

result returned).
        def newmethod(method,name):
            def _newmethod(self,*a,**k):
                old_currentinfo = current.info
                current.info = (cls,self,name,a,k) # update current.info
                rv = method(self,*a,**k) # call the original method
                current.info = old_currentinfo # reset current.info to the state in which it was before executing the method
                return rv
            _newmethod.__name__ = method.__name__
            return _newmethod
        # I didn't use inspect.getmembers because I'm just iterating over the methods actually defined in the class body, not over the inherited ones.
        for name in dct:
            m = getattr(cls,name)
            if ismethod(m):
                    setattr(cls,name,newmethod(m,name)) # subclass the method m with the function returned by newmethod (see comments for newmethod)

def upobject():
    """Return super(cls,self), where cls and self are alements of current.info."""
    cls,self = current.info[:2]
    return super(cls,self)

def upmethod():
    """Return upobject().METHOD, where METHOD is the name of the method that is currently executing."""
    return getattr(upobject(),current.info[2])

def upcall(*a,**k):
    """Shortcut for upmethod()(*a,**k)."""
    return upmethod()(*a,**k)

def delegate():
    """Execute and returns the result of upcall(*args,**kwargs) , where args and kwargs are elements of current.info ."""
    a,k = current.info[3:]
    return upmethod()(*a,**k)

class __up_class(object):
    """up is a light wrapper for the super object returned by upobject() . 'up.x' is the same as 'upobject().x' ; 'up.x=y' is the same as 'upobject().x=y."""
    def __getattribute__(self,name):
        return getattr(upobject(),name)
    def __setattr__(self,name,value):
        setattr(upobject(),name,value)

up = __up_class() # see __up_class doc

### TEST ###

from threading import Thread

class A:
    __metaclass__ = auto
    x = "A.x"
    def foo(self,x):
        print "A.foo(%s,%s) ; A.x = %s ;\n\tcurrent.info = %s"%(self,x,A.x,current.info)

class B(A):
    x = "B.x"
    def foo(self,x):
        delegate()
        print "B.foo(%s,%s) ; up.x = %s ;\n\tcurrent.info = %s"%(self,x,up.x,current.info)

class C(A):
    x = "C.x"
    def foo(self,x):
        print "C.foo(%s,%s) ; up.x = %s ;\n\tcurrent.info = %s"%(self,x,up.x,current.info)
        upcall(x*10)

class D(B,C):
    x = "C.x"
    def foo(self,x):
        print "D.foo(%s,%s) ; up.x = %s ;\n\tcurrent.info = %s"%(self,x,up.x,current.info)
        delegate()

d = D()
d.foo(10)

##    OUTPUT:
##
##    D.foo(<__main__.D object at 0x00BAD0F0>,10) ; up.x = B.x ;
##            current.info = (<class '__main__.D'>, <__main__.D object at 0x00BAD0F0>, 'foo', (10,), {})
##    C.foo(<__main__.D object at 0x00BAD0F0>,10) ; up.x = A.x ;
##            current.info = (<class '__main__.C'>, <__main__.D object at 0x00BAD0F0>, 'foo', (10,), {})
##    A.foo(<__main__.D object at 0x00BAD0F0>,100) ; A.x = A.x ;
##            current.info = (<class '__main__.A'>, <__main__.D object at 0x00BAD0F0>, 'foo', (100,), {})
##    B.foo(<__main__.D object at 0x00BAD0F0>,10) ; up.x = C.x ;
##            current.info = (<class '__main__.B'>, <__main__.D object at 0x00BAD0F0>, 'foo', (10,), {})

History

  • revision 3 (18 years ago)
  • previous revisions are not available