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
|
Operator overloading is best done with metaclasses:
::
#<autowrap.py>
import inspect
class wrappedmethod(Customizable):
"""Customizable method factory intended for derivation.
The wrapper method is overridden in the children."""
logfile=sys.stdout # default
namespace='' # default
def __new__(cls,meth): # meth is a descriptor
if isinstance(meth,FunctionType):
kind=0 # regular method
func=meth
elif isinstance(meth,staticmethod):
kind=1 # static method
func=meth.__get__('whatever')
elif isinstance(meth,classmethod):
kind=2 # class method
func=meth.__get__('whatever','whatever').im_func
elif isinstance(meth,wrappedmethod): # already wrapped
return meth # do nothing
elif inspect.ismethoddescriptor(meth):
kind=0; func=meth # for many builtin methods
else:
return meth # do nothing
self=super(wrappedmethod,cls).__new__(cls)
self.kind=kind; self.func=func # pre-initialize
return self
def __init__(self,meth): # meth not used
self.logfile=self.logfile # default values
self.namespace=self.namespace # copy the current
def __get__(self,obj,cls): # closure
def _(*args,**kw):
if obj is None: o=() # unbound method call
else: o=(obj,) # bound method call
allargs=[o,(),(cls,)][self.kind]+args
return self.wrapper()(*allargs,**kw)
return _ # the wrapped function
# allargs is the only nontrivial line in _; it adds
# 0 - obj if meth is a regular method
# 1 - nothing if meth is a static method
# 2 - cls if meth is a class method
def wrapper(self): return self.func # do nothing, to be overridden
class autowrappedmethod(wrappedmethod):
"""Makes the method returning cls instances, by wrapping its
output with cls"""
klass=None # has to be fixed dynamically from outside
def __init__(self,meth):
super(autowrappedmethod,self).__init__(meth) # cooperative
self.klass=self.klass # class variable -> instance variable
def wrapper(self): # closure
return lambda *args,**kw: self.klass(self.func(*args,**kw))
class AutoWrapped(type):
"""Metaclass that looks at the methods declared in the attributes
builtinlist and wraplist of its instances and wraps them with
autowrappedmethod."""
def __init__(cls,name,bases,dic):
super(AutoWrapped,cls).__init__(name,bases,dic) # cooperative
cls.builtinlist=getattr(cls,'builtinlist',[])
if not hasattr(cls,'diclist') : # true only at the first call
cls.diclist=[(a,vars(bases[0])[a]) for a in cls.builtinlist]
if dic.has_key('wraplist'): # can be true at any call
cls.diclist+=[(a,dic[a]) for a in cls.wraplist]
wrapper=autowrappedmethod.With(klass=cls)
d=dict([(a,wrapper(v)) for a,v in cls.diclist])
customize(cls,**d)
class Str(str):
__metaclass__=AutoWrapped
builtinlist="""__add__ __mod__ __mul__ __rmod__ __rmul__ capitalize
center expandtabs join ljust lower lstrip replace rjust rstrip strip
swapcase title translate upper zfill""".split()
#</autowrap.py>
Here I show various tests.
.. doctest
>>> from autowrap import Str
>>> sum=Str('a')+Str('b') # check the sum
>>> print sum, type(sum)
ab <class 'autowrap.Str'>
>>> rprod=Str('a')*2 # check the right product
>>> print rprod,type(rprod)
aa <class 'autowrap.Str'>
>>> lprod=2*Str('a') # check the left product
>>> print lprod,type(lprod)
aa <class 'autowrap.Str'>
>>> r=Str('a').replace('a','b') # check replace
>>> print r,type(r)
b <class 'autowrap.Str'>
>>> r=Str('a').capitalize() # check capitalize
>>> print r,type(r)
A <class 'autowrap.Str'>
|