Package gmisclib :: Module g_closure
[frames] | no frames]

Source Code for Module gmisclib.g_closure

  1  """This module defines closures. 
  2  Closures are functions that have been supplied with some arguments, 
  3  but not all of them. 
  4  """ 
  5   
  6   
  7   
8 -class NotYet:
9 """Just a marker, indicating that a certain argument is not yet 10 present, and the actual call should be deferred until later. 11 """ 12
13 - def __repr__(self):
14 return "<BLANK>"
15 16
17 -class ArgUnspecifiedError(TypeError):
18 - def __init__(self, s):
19 TypeError.__init__(self, s)
20 21 22
23 -class Closure:
24 - def __init__(self, fcn, *argv, **kwarg):
25 """Create a closure by memorizing a function and its 26 arguments. If all arguments are supplied, you 27 can get the result by calling the closure without arguments. 28 Missing arguments are replaced by instances of class NotYet; 29 such arguments will need to be supplied when the closure is 30 called. 31 32 As an additional feature, you can supply a list of users 33 of the function's value. When the value is (eventually) 34 produced, the user function(s) will be called with the 35 closure's return value. Users must be functions of one 36 argument. 37 """ 38 self.fcn = fcn 39 self.args = argv 40 self.argd = kwarg 41 self.deferred = [] 42 self.to_be_filled = [] 43 for (i, arg) in enumerate(argv): 44 if arg is NotYet: 45 self.to_be_filled.append(i)
46
47 - def __repr__(self):
48 return '%s(%s)' % (str(self.fcn), 49 ','.join([str(q) for q in self.args] 50 + ["%s=%s" % kv for kv in self.argd.items()]) 51 )
52
53 - def __call__(self, *argv, **argd):
54 """This evaluates the closure to either produce a value 55 or raise an exception. 56 """ 57 lstbf = len(self.to_be_filled) 58 if len(argv) < lstbf: 59 raise ArgUnspecifiedError, len(argv) 60 a = list(self.args) 61 for (tmparg,j) in zip(argv, self.to_be_filled): 62 a[j] = tmparg 63 a.extend( argv[lstbf:] ) 64 adict = self.argd.copy() 65 adict.update(argd) 66 rv = self.fcn(*a, **adict) 67 for user in self.deferred: 68 user(rv) 69 return rv
70
71 - def partial(self, *argv, **argd):
72 """This evaluates the closure to produce another 73 (more complete) closure. Users are not preserved. 74 """ 75 a = list(self.args) 76 for (tmparg,j) in zip(argv, self.to_be_filled): 77 a[j] = tmparg 78 lstbf = len(self.to_be_filled) 79 a.extend( argv[lstbf:] ) 80 adict = self.argd.copy() 81 adict.update(argd) 82 rv = Closure(self.fcn, *a, **adict) 83 return rv
84
85 - def defer(self, user):
86 """This will call the specified function when the closure 87 is completed. It's a bit of a kluge. 88 """ 89 self.deferred.append( user )
90 91 92 93 94 if __name__ == '__main__':
95 - def foo(a, b):
96 return a+b
97 98 x = Closure(foo) 99 assert x(1, 3)==4 100 101 x = Closure(foo, 3) 102 assert x(4)==7 103 104 try: 105 x(1, 1) 106 except TypeError: 107 pass 108 else: 109 print "Whoops! Expected TypeError" 110 111 try: 112 x() 113 except TypeError: 114 pass 115 else: 116 print "Whoops! Expected TypeError" 117 118 assert x(b=7)==10 119 120 try: 121 x(a=7) 122 except TypeError: 123 pass 124 else: 125 print "Whoops! Expected TypeError" 126 127
128 - def bar(a, b=8):
129 return a*b
130 131 y = Closure(bar) 132 assert y(a=1)==8 133 assert y(a=1,b=2)==2 134 135 y = Closure(bar, 3, 3) 136 assert y()==9 137 138 y = Closure(bar, NotYet, 7) 139 140 try: 141 y() 142 except TypeError: 143 pass 144 else: 145 print "Whoops! Expected TypeError" 146 147 assert y(7)==49 148 z = y.partial() 149 assert z(7)==49 150 z = y.partial(8) 151 assert z()==56
152 - def user1(x):
153 raise RuntimeError, str(x)
154 z.defer(user1) 155 try: 156 z() 157 except RuntimeError, x: 158 assert str(x) == "56" 159 pass 160