thirdparty/google_appengine/google/appengine/runtime/apiproxy.py
changeset 109 620f9b141567
child 828 f5fd65cc3bf3
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # Copyright 2007 Google Inc.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 #
       
     9 #     http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 #
       
    17 
       
    18 """Makes API calls to various Google-provided services.
       
    19 
       
    20 Provides methods for making calls into Google Apphosting services and APIs
       
    21 from your application code. This code will only work properly from within
       
    22 the Google Apphosting environment.
       
    23 """
       
    24 
       
    25 
       
    26 import sys
       
    27 from google.net.proto import ProtocolBuffer
       
    28 from google.appengine import runtime
       
    29 from google.appengine.runtime import apiproxy_errors
       
    30 from google3.apphosting.runtime import _apphosting_runtime___python__apiproxy
       
    31 
       
    32 OK                =  0
       
    33 RPC_FAILED        =  1
       
    34 CALL_NOT_FOUND    =  2
       
    35 ARGUMENT_ERROR    =  3
       
    36 DEADLINE_EXCEEDED =  4
       
    37 CANCELLED         =  5
       
    38 APPLICATION_ERROR =  6
       
    39 OTHER_ERROR       =  7
       
    40 OVER_QUOTA        =  8
       
    41 REQUEST_TOO_LARGE =  9
       
    42 CAPABILITY_DISABLED = 10
       
    43 
       
    44 _ExceptionsMap = {
       
    45   RPC_FAILED:
       
    46   (apiproxy_errors.RPCFailedError,
       
    47    "The remote RPC to the application server failed for the call %s.%s()."),
       
    48   CALL_NOT_FOUND:
       
    49   (apiproxy_errors.CallNotFoundError,
       
    50    "The API package '%s' or call '%s()' was not found."),
       
    51   ARGUMENT_ERROR:
       
    52   (apiproxy_errors.ArgumentError,
       
    53    "An error occurred parsing (locally or remotely) the arguments to %s.%s()."),
       
    54   DEADLINE_EXCEEDED:
       
    55   (apiproxy_errors.DeadlineExceededError,
       
    56    "The API call %s.%s() took too long to respond and was cancelled."),
       
    57   CANCELLED:
       
    58   (apiproxy_errors.CancelledError,
       
    59    "The API call %s.%s() was explicitly cancelled."),
       
    60   OTHER_ERROR:
       
    61   (apiproxy_errors.Error,
       
    62    "An error occurred for the API request %s.%s()."),
       
    63   OVER_QUOTA:
       
    64   (apiproxy_errors.OverQuotaError,
       
    65   "The API call %s.%s() required more quota than is available."),
       
    66   REQUEST_TOO_LARGE:
       
    67   (apiproxy_errors.RequestTooLargeError,
       
    68   "The request to API call %s.%s() was too large."),
       
    69 
       
    70 
       
    71 
       
    72 
       
    73 
       
    74 
       
    75 }
       
    76 
       
    77 class RPC(object):
       
    78   """A RPC object, suitable for talking to remote services.
       
    79 
       
    80   Each instance of this object can be used only once, and should not be reused.
       
    81 
       
    82   Stores the data members and methods for making RPC calls via the APIProxy.
       
    83   """
       
    84 
       
    85   IDLE = 0
       
    86   RUNNING = 1
       
    87   FINISHING = 2
       
    88 
       
    89   def __init__(self, package=None, call=None, request=None, response=None,
       
    90                callback=None):
       
    91     """Constructor for the RPC object. All arguments are optional, and
       
    92     simply set members on the class. These data members will be
       
    93     overriden by values passed to MakeCall.
       
    94 
       
    95     Args:
       
    96       package: string, the package for the call
       
    97       call: string, the call within the package
       
    98       request: ProtocolMessage instance, appropriate for the arguments
       
    99       response: ProtocolMessage instance, appropriate for the response
       
   100       callback: callable, called when call is complete
       
   101     """
       
   102     self.__exception = None
       
   103     self.__traceback = None
       
   104     self.__result_dict = {}
       
   105     self.__state = RPC.IDLE
       
   106 
       
   107     self.package = package
       
   108     self.call = call
       
   109     self.request = request
       
   110     self.response = response
       
   111     self.callback = callback
       
   112 
       
   113 
       
   114   def MakeCall(self, package=None, call=None, request=None, response=None,
       
   115                callback=None):
       
   116     """Makes an asynchronous (i.e. non-blocking) API call within the
       
   117     specified package for the specified call method. request and response must
       
   118     be the appropriately typed ProtocolBuffers for the API call.
       
   119     callback, if provided, will be called when the request completes
       
   120     successfully or when an error occurs.  If an error has ocurred, the
       
   121     exception() method on this class will return the error, which can be
       
   122     accessed from the callback.
       
   123 
       
   124     Args:
       
   125       Same as constructor; see __init__.
       
   126 
       
   127     Raises:
       
   128       TypeError or AssertionError if an argument is of an invalid type.
       
   129       AssertionError or RuntimeError is an RPC is already in use.
       
   130     """
       
   131     self.callback = callback or self.callback
       
   132     self.package = package or self.package
       
   133     self.call = call or self.call
       
   134     self.request = request or self.request
       
   135     self.response = response or self.response
       
   136 
       
   137     assert self.__state is RPC.IDLE, ("RPC for %s.%s has already been started" %
       
   138                                       (self.package, self.call))
       
   139     assert self.callback is None or callable(self.callback)
       
   140     assert isinstance(self.request, ProtocolBuffer.ProtocolMessage)
       
   141     assert isinstance(self.response, ProtocolBuffer.ProtocolMessage)
       
   142 
       
   143     e = ProtocolBuffer.Encoder()
       
   144     self.request.Output(e)
       
   145 
       
   146     self.__state = RPC.RUNNING
       
   147     _apphosting_runtime___python__apiproxy.MakeCall(
       
   148         self.package, self.call, e.buffer(), self.__result_dict,
       
   149         self.__MakeCallDone, self)
       
   150 
       
   151   def Wait(self):
       
   152     """Waits on the API call associated with this RPC. The callback,
       
   153     if provided, will be executed before Wait() returns. If this RPC
       
   154     is already complete, or if the RPC was never started, this
       
   155     function will return immediately.
       
   156 
       
   157     Raises:
       
   158       InterruptedError if a callback throws an uncaught exception.
       
   159     """
       
   160     try:
       
   161       rpc_completed = _apphosting_runtime___python__apiproxy.Wait(self)
       
   162     except runtime.DeadlineExceededError:
       
   163       raise
       
   164     except apiproxy_errors.InterruptedError:
       
   165       raise
       
   166     except:
       
   167       exc_class, exc, tb = sys.exc_info()
       
   168       if (isinstance(exc, SystemError) and
       
   169           exc.args == ('uncaught RPC exception',)):
       
   170         raise
       
   171       rpc = None
       
   172       if hasattr(exc, "_appengine_apiproxy_rpc"):
       
   173         rpc = exc._appengine_apiproxy_rpc
       
   174       new_exc = apiproxy_errors.InterruptedError(exc, rpc)
       
   175       raise new_exc.__class__, new_exc, tb
       
   176 
       
   177     assert rpc_completed, ("RPC for %s.%s was not completed, and no other " +
       
   178                            "exception was raised " % (self.package, self.call))
       
   179 
       
   180   def CheckSuccess(self):
       
   181     """If there was an exception, raise it now.
       
   182 
       
   183     Raises:
       
   184       Exception of the API call or the callback, if any.
       
   185     """
       
   186     if self.exception and self.__traceback:
       
   187       raise self.exception.__class__, self.exception, self.__traceback
       
   188     if self.exception:
       
   189       raise self.exception
       
   190 
       
   191   @property
       
   192   def exception(self):
       
   193     return self.__exception
       
   194 
       
   195   @property
       
   196   def state(self):
       
   197     return self.__state
       
   198 
       
   199   def __MakeCallDone(self):
       
   200     self.__state = RPC.FINISHING
       
   201     if self.__result_dict['error'] == APPLICATION_ERROR:
       
   202       self.__exception = apiproxy_errors.ApplicationError(
       
   203           self.__result_dict['application_error'],
       
   204           self.__result_dict['error_detail'])
       
   205     elif self.__result_dict['error'] == CAPABILITY_DISABLED:
       
   206       if self.__result_dict['error_detail']:
       
   207         self.__exception = apiproxy_errors.CapabilityDisabledError(
       
   208             self.__result_dict['error_detail'])
       
   209       else:
       
   210         self.__exception = apiproxy_errors.CapabilityDisabledError(
       
   211             "The API call %s.%s() is temporarily unavailable." % (
       
   212             self.package, self.call))
       
   213     elif _ExceptionsMap.has_key(self.__result_dict['error']):
       
   214       exception_entry = _ExceptionsMap[self.__result_dict['error']]
       
   215       self.__exception = exception_entry[0](
       
   216           exception_entry[1] % (self.package, self.call))
       
   217     else:
       
   218       try:
       
   219         self.response.ParseFromString(self.__result_dict['result_string'])
       
   220       except Exception, e:
       
   221         self.__exception = e
       
   222     if self.callback:
       
   223       try:
       
   224         self.callback()
       
   225       except:
       
   226         exc_class, self.__exception, self.__traceback = sys.exc_info()
       
   227         self.__exception._appengine_apiproxy_rpc = self
       
   228         raise
       
   229 
       
   230 
       
   231 def MakeSyncCall(package, call, request, response):
       
   232   """Makes a synchronous (i.e. blocking) API call within the specified
       
   233   package for the specified call method. request and response must be the
       
   234   appropriately typed ProtocolBuffers for the API call. An exception is
       
   235   thrown if an error occurs when communicating with the system.
       
   236 
       
   237   Args:
       
   238     See MakeCall above.
       
   239 
       
   240   Raises:
       
   241     See CheckSuccess() above.
       
   242   """
       
   243   rpc = RPC()
       
   244   rpc.MakeCall(package, call, request, response)
       
   245   rpc.Wait()
       
   246   rpc.CheckSuccess()