237 stub = self.GetStub(service) |
241 stub = self.GetStub(service) |
238 assert stub, 'No api proxy found for service "%s"' % service |
242 assert stub, 'No api proxy found for service "%s"' % service |
239 self.__precall_hooks.Call(service, call, request, response) |
243 self.__precall_hooks.Call(service, call, request, response) |
240 stub.MakeSyncCall(service, call, request, response) |
244 stub.MakeSyncCall(service, call, request, response) |
241 self.__postcall_hooks.Call(service, call, request, response) |
245 self.__postcall_hooks.Call(service, call, request, response) |
|
246 |
|
247 |
|
248 class UserRPC(object): |
|
249 """Wrapper class for asynchronous RPC. |
|
250 |
|
251 Simplest low-level usage pattern: |
|
252 |
|
253 rpc = UserRPC('service', [deadline], [callback]) |
|
254 rpc.make_call('method', request, response) |
|
255 . |
|
256 . |
|
257 . |
|
258 rpc.wait() |
|
259 rpc.check_success() |
|
260 |
|
261 However, a service module normally provides a wrapper so that the |
|
262 typical usage pattern becomes more like this: |
|
263 |
|
264 from google.appengine.api import service |
|
265 rpc = service.create_rpc([deadline], [callback]) |
|
266 service.make_method_call(rpc, [service-specific-args]) |
|
267 . |
|
268 . |
|
269 . |
|
270 rpc.wait() |
|
271 result = rpc.get_result() |
|
272 |
|
273 The service.make_method_call() function sets a service- and method- |
|
274 specific hook function that is called by rpc.get_result() with the |
|
275 rpc object as its first argument, and service-specific value as its |
|
276 second argument. The hook function should call rpc.check_success() |
|
277 and then extract the user-level result from the rpc.result |
|
278 protobuffer. Additional arguments may be passed from |
|
279 make_method_call() to the get_result hook via the second argument. |
|
280 """ |
|
281 |
|
282 __method = None |
|
283 __get_result_hook = None |
|
284 __user_data = None |
|
285 __postcall_hooks_called = False |
|
286 |
|
287 def __init__(self, service, deadline=None, callback=None): |
|
288 """Constructor. |
|
289 |
|
290 Args: |
|
291 service: The service name. |
|
292 deadline: Optional deadline. Default depends on the implementation. |
|
293 callback: Optional argument-less callback function. |
|
294 """ |
|
295 self.__service = service |
|
296 self.__rpc = CreateRPC(service) |
|
297 self.__rpc.deadline = deadline |
|
298 self.__rpc.callback = callback |
|
299 |
|
300 @property |
|
301 def service(self): |
|
302 """Return the service name.""" |
|
303 return self.__service |
|
304 |
|
305 @property |
|
306 def method(self): |
|
307 """Return the method name.""" |
|
308 return self.__method |
|
309 |
|
310 @property |
|
311 def deadline(self): |
|
312 """Return the deadline, if set explicitly (otherwise None).""" |
|
313 return self.__rpc.deadline |
|
314 |
|
315 def __get_callback(self): |
|
316 """Return the callback attribute, a function without arguments. |
|
317 |
|
318 This attribute can also be assigned to. For example, the |
|
319 following code calls some_other_function(rpc) when the RPC is |
|
320 complete: |
|
321 |
|
322 rpc = service.create_rpc() |
|
323 rpc.callback = lambda: some_other_function(rpc) |
|
324 service.make_method_call(rpc) |
|
325 rpc.wait() |
|
326 """ |
|
327 return self.__rpc.callback |
|
328 def __set_callback(self, callback): |
|
329 """Set the callback function.""" |
|
330 self.__rpc.callback = callback |
|
331 callback = property(__get_callback, __set_callback) |
|
332 |
|
333 @property |
|
334 def request(self): |
|
335 """Return the request protocol buffer object.""" |
|
336 return self.__rpc.request |
|
337 |
|
338 @property |
|
339 def response(self): |
|
340 """Return the response protocol buffer object.""" |
|
341 return self.__rpc.response |
|
342 |
|
343 @property |
|
344 def state(self): |
|
345 """Return the RPC state. |
|
346 |
|
347 Possible values are attributes of apiproxy_rpc.RPC: IDLE, RUNNING, |
|
348 FINISHING. |
|
349 """ |
|
350 return self.__rpc.state |
|
351 |
|
352 @property |
|
353 def get_result_hook(self): |
|
354 """Return the get-result hook function.""" |
|
355 return self.__get_result_hook |
|
356 |
|
357 @property |
|
358 def user_data(self): |
|
359 """Return the user data for the hook function.""" |
|
360 return self.__user_data |
|
361 |
|
362 def make_call(self, method, request, response, |
|
363 get_result_hook=None, user_data=None): |
|
364 """Initiate a call. |
|
365 |
|
366 Args: |
|
367 method: The method name. |
|
368 request: The request protocol buffer. |
|
369 response: The response protocol buffer. |
|
370 get_result_hook: Optional get-result hook function. If not None, |
|
371 this must be a function with exactly one argument, the RPC |
|
372 object (self). Its return value is returned from get_result(). |
|
373 user_data: Optional additional arbitrary data for the get-result |
|
374 hook function. This can be accessed as rpc.user_data. The |
|
375 type of this value is up to the service module. |
|
376 |
|
377 This function may only be called once per RPC object. It sends |
|
378 the request to the remote server, but does not wait for a |
|
379 response. This allows concurrent execution of the remote call and |
|
380 further local processing (e.g., making additional remote calls). |
|
381 |
|
382 Before the call is initiated, the precall hooks are called. |
|
383 """ |
|
384 assert self.__rpc.state == apiproxy_rpc.RPC.IDLE, repr(self.state) |
|
385 self.__method = method |
|
386 self.__get_result_hook = get_result_hook |
|
387 self.__user_data = user_data |
|
388 apiproxy.GetPreCallHooks().Call(self.__service, method, request, response) |
|
389 self.__rpc.MakeCall(self.__service, method, request, response) |
|
390 |
|
391 def wait(self): |
|
392 """Wait for the call to complete, and call callbacks. |
|
393 |
|
394 This is the only time callback functions may be called. (However, |
|
395 note that check_success() and get_result() call wait().) Waiting |
|
396 for one RPC may cause callbacks for other RPCs to be called. |
|
397 Callback functions may call check_success() and get_result(). |
|
398 |
|
399 Callbacks are called without arguments; if a callback needs access |
|
400 to the RPC object a Python nested function (a.k.a. closure) or a |
|
401 bound may be used. To facilitate this, the callback may be |
|
402 assigned after the RPC object is created (but before make_call() |
|
403 is called). |
|
404 |
|
405 Note: don't confuse callbacks with get-result hooks or precall |
|
406 and postcall hooks. |
|
407 """ |
|
408 assert self.__rpc.state != apiproxy_rpc.RPC.IDLE, repr(self.state) |
|
409 if self.__rpc.state == apiproxy_rpc.RPC.RUNNING: |
|
410 self.__rpc.Wait() |
|
411 assert self.__rpc.state == apiproxy_rpc.RPC.FINISHING, repr(self.state) |
|
412 |
|
413 def check_success(self): |
|
414 """Check for success of the RPC, possibly raising an exception. |
|
415 |
|
416 This function should be called at least once per RPC. If wait() |
|
417 hasn't been called yet, it is called first. If the RPC caused |
|
418 an exceptional condition, an exception will be raised here. |
|
419 The first time check_success() is called, the postcall hooks |
|
420 are called. |
|
421 """ |
|
422 self.wait() |
|
423 self.__rpc.CheckSuccess() |
|
424 if not self.__postcall_hooks_called: |
|
425 self.__postcall_hooks_called = True |
|
426 apiproxy.GetPostCallHooks().Call(self.__service, self.__method, |
|
427 self.request, self.response) |
|
428 |
|
429 def get_result(self): |
|
430 """Get the result of the RPC, or possibly raise an exception. |
|
431 |
|
432 This implies a call to check_success(). If a get-result hook was |
|
433 passed to make_call(), that hook is responsible for calling |
|
434 check_success(), and the return value of the hook is returned. |
|
435 Otherwise, check_success() is called directly and None is |
|
436 returned. |
|
437 """ |
|
438 if self.__get_result_hook is None: |
|
439 self.check_success() |
|
440 return None |
|
441 else: |
|
442 return self.__get_result_hook(self) |
242 |
443 |
243 |
444 |
244 def GetDefaultAPIProxy(): |
445 def GetDefaultAPIProxy(): |
245 try: |
446 try: |
246 runtime = __import__('google.appengine.runtime', globals(), locals(), |
447 runtime = __import__('google.appengine.runtime', globals(), locals(), |
247 ['apiproxy']) |
448 ['apiproxy']) |
248 return APIProxyStubMap(runtime.apiproxy) |
449 return APIProxyStubMap(runtime.apiproxy) |
249 except (AttributeError, ImportError): |
450 except (AttributeError, ImportError): |
250 return APIProxyStubMap() |
451 return APIProxyStubMap() |
251 |
452 |
|
453 |
252 apiproxy = GetDefaultAPIProxy() |
454 apiproxy = GetDefaultAPIProxy() |