1 import logging
2 from Fault import current_tb_message
3 from Message import Message
4 from functools import wraps
5 from grassyknoll.lib.meta import FactoryMixin
6
7
8
10 """a representation of a function call
11
12 Use this with L{functionMessenger} and L{methodMessenger}.
13
14 @ivar args: *args for the method
15 @type args: list
16
17 @ivar kwargs: **kwargs for the method
18 @type kwargs: dict
19 """
20 __slots__=['args', 'kwargs']
21
25
27 return "FunctionCall(%r, %r)"%(self.args, self.kwargs)
28
30 """a represention of a method to be called on object.
31
32 Use this with L{objectMessenger}.
33
34 @ivar method: the name of the method
35 @type method: string
36 """
37
38 __slots__=['method_name']
39
40 - def __init__(self, method_name, *args, **kwargs):
43
45 return "MethodCall(%r, %r, %r)"%(self.method_name, self.args, self.kwargs)
46
48 """wrap a L{func} so that it can handle L{Message}s.
49
50 The resulting L{messenger} function takes messages with L{FuncCall} as a
51 payload. L{messenger} calls the decorated L{func} and stuffs the return
52 value in a reply message. If the original message does not specify a
53 L{Message.reply_box}, the return value will be discarded.
54
55 @arg send_none: if true and L{func} returns None, use None as the reply's
56 L{Message.payload}. Otherwise, do not return a message.
57 @type send_none: boolean
58 """
59
60 def wrapper(func):
61 @wraps(func)
62 def messenger(mesg):
63 args=mesg.payload.args
64 kwargs=mesg.payload.kwargs
65 ret=func(*args, **kwargs)
66 if ret is not None or send_none:
67 return Message(ret, re_id=mesg.id)
68 return messenger
69 return wrapper
70
72 """wrap a L{method} so that it can handle L{Message}s.
73
74 The resulting L{messenger} function takes messages with L{FuncCall} as a
75 payload. L{messenger} calls the decorated L{method} and stuffs the return
76 value in a reply message. If the original message does not specify a
77 L{Message.reply_box}, the return value will be discarded.
78
79 @arg send_none: if true and L{method} returns None, use None as the
80 reply's L{Message.payload}. Otherwise, do not return a message.
81 @type send_none: boolean
82
83 @warning: This wrapper does not provide thread safety; you should use it
84 only on read-only objects.
85 """
86
87 def wrapper(method):
88 @wraps(method)
89 def messenger(self, mesg):
90 args=mesg.payload.args
91 kwargs=mesg.payload.kwargs
92 ret=method(self, *args, **kwargs)
93 if ret is not None or send_none:
94 return Message(ret, re_id=mesg.id)
95 return messenger
96 return wrapper
97
99 """wrap an L{obj} so that it can handle L{Message}s by dispatching to
100 methods.
101
102 The resulting L{messenger} function takes messages with L{MethodCall} as a
103 payload. L{messenger} calls the appropriate C{obj.method} and stuffs the
104 return value in a reply message. If the original message does not specify
105 a L{Message.reply_box}, the return value will be discarded.
106
107 @arg obj: the instance to be wrapped
108 @type obj: instance
109
110 @arg send_none: if true and L{func} returns None, use None as the reply's
111 L{Message.payload}. Otherwise, do not return a message.
112 @type send_none: boolean
113 """
114
115 def messenger(mesg):
116 name=mesg.payload.method_name
117 method=getattr(obj, name)
118 args=mesg.payload.args
119 kwargs=mesg.payload.kwargs
120 ret=method(*args, **kwargs)
121 if ret is not None or send_none:
122 return Message(ret, re_id=mesg.id)
123
124 return messenger
125
127 """wrap a L{func} so that it returns L{Message}s on errors
128
129 The resulting L{messenger} function takes messages and calls the decorated
130 L{func}. If func raises an exception in L{exception_types}, the exception
131 is packed in a L{Fault.Fault} in a reply message.
132
133
134 If the original message does not specify a L{Message.reply_box}, the
135 exception value will be discarded. Exceptions not in L{exception_types}
136 will be raise normally.
137
138 @arg exception_types: types of exceptions to catch. These are meant as
139 intentionally raised exceptions, as opposed to unhandled (programmer)
140 errors.
141 @type exception_types: classes
142
143 @arg include_tb: include a textified traceback in the message, see
144 L{current_tb_message}
145 @type include_tb: boolean
146 """
147 exception_types=tuple(exception_types)
148
149 def wrapper(func):
150 @wraps(func)
151 def messenger(mesg):
152 try:
153 ret=func(mesg)
154 except exception_types:
155 return current_tb_message(re_id=mesg.id)
156 else:
157 return ret
158 return messenger
159 return wrapper
160
161 __debug_logger=logging.getLogger()
163 """wrap a L{func} with debugging.
164
165 The resulting L{messenger} function will log the messages and replies of
166 the decorated L{func}.
167 """
168 @wraps(func)
169 def messenger(mesg):
170 __debug_logger.debug("called %r", mesg)
171 try:
172 ret=func(mesg)
173 except Exception, e:
174 __debug_logger.debug("raised %r", e)
175 raise
176 else:
177 __debug_logger.debug("return %r", ret)
178 return ret
179 return messenger
180
181 -class NullContextManager(FactoryMixin):
182 """a null U{context manager<http://www.python.org/dev/peps/pep-0343/>}
183 that simply returns what it was constructed with.
184
185 @arg func: the func to wrap
186 @type func: callable
187 """
188
189 __slots__=['func']
190
191 - def __init__(self, func):
193
194 - def __enter__(self):
196
197 - def __exit__(self, type, value, traceback):
199
200 -class FactoryContextManager(FactoryMixin):
201 """a U{context manager<http://www.python.org/dev/peps/pep-0343/>} for
202 working with factories.
203
204 L{factory} will be called and should return an object with a C{close}
205 method, which will be called from C{__exit__}.
206
207 L{decorators} is a list of functions applied in natural order (ie,
208 right-to-left) to the object. The return value of the final decorator will
209 be returned from C{__enter__}.
210
211 @ivar decorators: a list of decorators to apply the object
212 @type decorators: list
213
214 @ivar obj: the object
215 @type obj: instance
216
217 @cvar default_decorators: a default set of decorators specified by
218 subclasses. These are applied before L{more_decorators}.
219 @type default_decorators: list of decorators
220 """
221
222 __slots__=['obj', 'decorators']
223
224 default_decorators=[]
225
226 - def __init__(self, factory, more_decorators):
227 """
228 @arg factory: a factory returning an object to be managed
229 @type factory: factory
230
231 @arg more_decorators: more decorators to apply to the object. These
232 are applied after L{default_decorators}.
233 @type more_decorators: list of decorators
234 """
235 self.obj=factory()
236 self.decorators=list(more_decorators)+self.default_decorators
237
238 - def __enter__(self):
239 o=self.obj
240 for w in self.decorators[::-1]:
241 o=w(o)
242 return o
243
244 - def __exit__(self, type, value, traceback):
246