Package Biskit :: Module decorators
[hide private]
[frames] | no frames]

Source Code for Module Biskit.decorators

  1  ## 
  2  ## Biskit, a toolkit for the manipulation of macromolecular structures 
  3  ## Copyright (C) 2004-2006 Raik Gruenberg & Johan Leckner 
  4  ## 
  5  ## This program is free software; you can redistribute it and/or 
  6  ## modify it under the terms of the GNU General Public License as 
  7  ## published by the Free Software Foundation; either version 2 of the 
  8  ## License, or any later version. 
  9  ## 
 10  ## This program is distributed in the hope that it will be useful, 
 11  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  ## General Public License for more details. 
 14  ## 
 15  ## You find a copy of the GNU General Public License in the file 
 16  ## license.txt along with this program; if not, write to the Free 
 17  ## Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 18  ## 
 19  ## 
 20  ## $Revision: 2.7 $ 
 21  ## last $Author: graik $ 
 22  ## last $Date: 2007/03/05 10:28: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 ## for methods of classes, expect 'self' at first position 55 if f.func_code.co_varnames[0] == 'self': 56 req = ( types.InstanceType, ) + req 57 58 ## check obligatory arguments 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 ## check optional arguments 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
83 -def synchronized( f ):
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] ## get parent object 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 ## TESTING 131 ############# 132 import Biskit.test as BT 133
134 -class Test(BT.BiskitTest):
135 """Test case""" 136
137 - def test_decorators( self ):
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