Welcome, guest | Sign In | My Account | Store | Cart

This is another way to deal with questions related to the "autosuper" topic. I have used a metaclass "auto" that keep track of every execution context for the methods in the classes it generates, allowing the user to write "upcall(args,kwargs)" from inside a method - say "mymethod" - to mean "super(C,self).mymethod(args,kwargs)", where C is the class the current method is defined in. It's even possible to write just "delegate()" instead of "super(C,self).mymethod(args,*kwargs)", when you want the method in the base class to be passed the same parameters the current methods are passed to.

Python, 110 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
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,), {})

The idea is to wrap each method found in the class by "subclassing it": the metaclass "auto" replace the original method with one that: 1) Store information about the original method context, such as the class in which the method is defined, the method name and the arguments passed to it; 2) Call the original method; 3) Restore previous information. This way, we "know" in each moment what method is being executed in what class, so that checking current.info from inside the body of a method will retrieve useful information, that we can utilize, for example, to see which class to pass to the super object. Note that keeping track of a class method execution context has been made thread-aware by making "info" an instance attribute for the object "current", whose class inherit from threading.local.

Created by Diego Novella on Sat, 7 May 2005 (PSF)
Python recipes (4591)
Diego Novella's recipes (3)

Required Modules

  • (none specified)

Other Information and Tasks