1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """
24 Defines method decorators. Decorators are wrapping functions
25 that can preceed the definition of methods (since Python 2.4, using the '@'
26 symbol) and enforce, for example, type-checking, or static calls.
27 Before each call, the decorator function replaces the original function by
28 some modified version. The details are described in the Python 2.4
29 documentation.
30 """
31
32 import types
33 import threading
34
35 -def accept( *required, **optional ):
36 """
37 Decorator function that enforces type checking on arguments of a method.
38
39 Example::
40 @accept( int, float, opt1=int, opt2=PDBModel )
41 def method( n, fraction, opt2=PDBModel(), **args ):
42 ...
43
44 The leading 'self' argument of class methods is automatically accepted and
45 the parent class of the method should thus B{not} be given as first type.
46 For keyword arguments, None is always an accepted value.
47 """
48 def wrapper( f ):
49
50 def new_f( *args, **kwds ):
51
52 req = required
53
54
55 if f.func_code.co_varnames[0] == 'self':
56 req = ( types.InstanceType, ) + req
57
58
59 for (type, given) in zip( req, args ):
60
61 assert isinstance(given, type), \
62 "argument %r does not match %s" % (given, type)
63
64
65 for (name, value) in kwds.items():
66
67 assert name in optional, \
68 "argument %s is not allowed" % name
69
70 if value is None: continue
71
72 assert isinstance(value, optional[name]), \
73 "argument %s does not match %s" % (name, optional[name])
74
75 return f( *args, **kwds )
76
77 new_f.func_name = f.func_name
78 return new_f
79
80 return wrapper
81
82
84 """
85 Decorator function ensuring that parallel threads call the wrapped
86 class method one after the other. That means, it is guaranteed
87 that this method of a given object is never executed in
88 parallel. However, different instances of the same class are not
89 blocked and can still call the routine in parallel.
90
91 Example::
92 @synchronized
93 def open_log_file( self, fname ):
94 ...
95
96 @note: The decorator adds (if not already present) a RLock object
97 'lock' and a Condition object 'lockMsg' to the object holding this
98 method. The wrapped method can hence call self.lockMsg.wait() or
99 self.lockMsg.notify/notifyAll() directly without any need for
100 acquiring a lock or creating a self.lockMsg.
101
102 For the same reason synchronized can only be applied to methods of objects.
103 """
104
105 def lock_call_release( *arg, **kw ):
106
107 parent = arg[0]
108 assert isinstance( parent, types.InstanceType ), \
109 'missing self argument'
110
111 if not hasattr( parent, 'lock' ):
112 parent.lock = threading.RLock()
113 if not hasattr( parent, 'lockMsg' ):
114 parent.lockMsg = threading.Condition()
115
116 parent.lock.acquire()
117
118 try:
119 result = f( *arg, **kw )
120 finally:
121
122 parent.lock.release()
123
124 return result
125
126 return lock_call_release
127
128
129
130
131
132 import Biskit.test as BT
133
134 -class Test(BT.BiskitTest):
135 """Test case"""
136
138 """decorators test"""
139 import time
140 from Biskit import PDBModel
141
142 class A:
143
144 def __init__( self, id=1 ):
145 self.id = id
146
147 @accept( int, model=PDBModel, x=str )
148 def simple( self, i, model=None, **arg ):
149 i += 1
150 return i
151
152 @synchronized
153 def report( self, j, i=1, **arg ):
154 print self.id
155
156
157 a = A()
158
159 t = time.clock()
160
161 result = 0
162 for i in range( 10000 ):
163
164 result += a.simple( 8, x='a' )
165
166
167 if self.local:
168 print 'timing: ', time.clock() - t
169 globals().update( locals() )
170
171 self.assertEqual( result, 90000 )
172
173
174 if __name__ == '__main__':
175
176 BT.localTest()
177