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
|
"""MagicSuper: an example of metaclass recompiling the source code.
This provides Python with a ``callsupermethod`` macro simplifying
the cooperative call syntax.
Examples:
from magicsuper import object
class B(object):
def __new__(cls, *args, **kw):
print "B.__new__"
return callsupermethod(cls)
def __init__(self, *args, **kw):
print "B.__init__"
callsupermethod(*args, **kw)
@staticmethod
def sm():
print "B.sm"
@classmethod
def cm(cls):
print cls.__name__
class C(B):
def __new__(cls, *args, **kw):
print args, kw
return callsupermethod(cls, *args, **kw)
@staticmethod
def sm():
callsupermethod()
@classmethod
def cm(cls):
callsupermethod()
c = C()
c.cm()
c.sm()
"""
import inspect, textwrap
class MagicSuper(type):
def __init__(cls, clsname, bases, dic):
clsmodule = __import__(cls.__module__) #assume cls is defined in source
for name, value in dic.iteritems():
# __new__ is seen as a function in the dic, so it has
# to be converted explicitly into a staticmethod;
# ordinary staticmethods don't type-dispatch on their
# first argument, so use 'super(cls, cls)' for them.
was_staticmethod = False
if isinstance(value, staticmethod):
value = value.__get__("dummy") # convert to function
was_staticmethod = True
elif isinstance(value, classmethod):
value = value.__get__("dummy").im_func # convert to function
if inspect.isfunction(value):
if was_staticmethod:
first_arg = clsname
else:
first_arg = inspect.getargspec(value)[0][0]
source = textwrap.dedent(inspect.getsource(value))
if not 'callsupermethod' in source: continue
source = source.replace(
'callsupermethod', 'super(%s, %s).%s'
% (clsname, first_arg, name))
#print source # debug
exec source in clsmodule.__dict__, dic # modifies dic
if name == "__new__":
dic[name] = staticmethod(dic[name])
setattr(cls, name, dic[name])
object = MagicSuper("object", (), {})
type = MagicSuper("type", (type,), {})
# example:
class B(object):
def __new__(cls, *args, **kw):
return callsupermethod(cls)
def __init__(self, *args, **kw):
print "B.__init__"
callsupermethod(*args, **kw)
@staticmethod
def sm():
print "B.sm"
@classmethod
def cm(cls):
print cls.__name__
class C(B):
def __new__(cls, *args, **kw):
print args, kw
return callsupermethod(cls, *args, **kw)
@staticmethod
def sm():
callsupermethod()
@classmethod
def cm(cls):
callsupermethod()
if __name__ == "__main__":
c = C(1, x=2)
c.sm()
c.cm()
|