72 |
73 |
73 |
74 |
74 |
75 |
75 } |
76 } |
76 |
77 |
77 class RPC(object): |
78 class RPC(apiproxy_rpc.RPC): |
78 """A RPC object, suitable for talking to remote services. |
79 """A RPC object, suitable for talking to remote services. |
79 |
80 |
80 Each instance of this object can be used only once, and should not be reused. |
81 Each instance of this object can be used only once, and should not be reused. |
81 |
82 |
82 Stores the data members and methods for making RPC calls via the APIProxy. |
83 Stores the data members and methods for making RPC calls via the APIProxy. |
83 """ |
84 """ |
84 |
85 |
85 IDLE = 0 |
86 def __init__(self, *args, **kargs): |
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 |
87 """Constructor for the RPC object. All arguments are optional, and |
92 simply set members on the class. These data members will be |
88 simply set members on the class. These data members will be |
93 overriden by values passed to MakeCall. |
89 overriden by values passed to MakeCall. |
|
90 """ |
|
91 super(RPC, self).__init__(*args, **kargs) |
|
92 self.__result_dict = {} |
94 |
93 |
95 Args: |
94 def _WaitImpl(self): |
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, |
95 """Waits on the API call associated with this RPC. The callback, |
153 if provided, will be executed before Wait() returns. If this RPC |
96 if provided, will be executed before Wait() returns. If this RPC |
154 is already complete, or if the RPC was never started, this |
97 is already complete, or if the RPC was never started, this |
155 function will return immediately. |
98 function will return immediately. |
156 |
99 |
157 Raises: |
100 Raises: |
158 InterruptedError if a callback throws an uncaught exception. |
101 InterruptedError if a callback throws an uncaught exception. |
159 """ |
102 """ |
160 try: |
103 try: |
161 rpc_completed = _apphosting_runtime___python__apiproxy.Wait(self) |
104 rpc_completed = _apphosting_runtime___python__apiproxy.Wait(self) |
162 except runtime.DeadlineExceededError: |
105 except (runtime.DeadlineExceededError, apiproxy_errors.InterruptedError): |
163 raise |
|
164 except apiproxy_errors.InterruptedError: |
|
165 raise |
106 raise |
166 except: |
107 except: |
167 exc_class, exc, tb = sys.exc_info() |
108 exc_class, exc, tb = sys.exc_info() |
168 if (isinstance(exc, SystemError) and |
109 if (isinstance(exc, SystemError) and |
169 exc.args == ('uncaught RPC exception',)): |
110 exc.args[0] == 'uncaught RPC exception'): |
170 raise |
111 raise |
171 rpc = None |
112 rpc = None |
172 if hasattr(exc, "_appengine_apiproxy_rpc"): |
113 if hasattr(exc, "_appengine_apiproxy_rpc"): |
173 rpc = exc._appengine_apiproxy_rpc |
114 rpc = exc._appengine_apiproxy_rpc |
174 new_exc = apiproxy_errors.InterruptedError(exc, rpc) |
115 new_exc = apiproxy_errors.InterruptedError(exc, rpc) |
175 raise new_exc.__class__, new_exc, tb |
116 raise new_exc.__class__, new_exc, tb |
|
117 return True |
176 |
118 |
177 assert rpc_completed, ("RPC for %s.%s was not completed, and no other " + |
119 def _MakeCallImpl(self): |
178 "exception was raised " % (self.package, self.call)) |
120 assert isinstance(self.request, ProtocolBuffer.ProtocolMessage) |
|
121 assert isinstance(self.response, ProtocolBuffer.ProtocolMessage) |
179 |
122 |
180 def CheckSuccess(self): |
123 e = ProtocolBuffer.Encoder() |
181 """If there was an exception, raise it now. |
124 self.request.Output(e) |
182 |
125 |
183 Raises: |
126 self.__state = RPC.RUNNING |
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 |
127 |
191 @property |
128 _apphosting_runtime___python__apiproxy.MakeCall( |
192 def exception(self): |
129 self.package, self.call, e.buffer(), self.__result_dict, |
193 return self.__exception |
130 self.__MakeCallDone, self) |
194 |
|
195 @property |
|
196 def state(self): |
|
197 return self.__state |
|
198 |
131 |
199 def __MakeCallDone(self): |
132 def __MakeCallDone(self): |
200 self.__state = RPC.FINISHING |
133 self.__state = RPC.FINISHING |
201 if self.__result_dict['error'] == APPLICATION_ERROR: |
134 if self.__result_dict['error'] == APPLICATION_ERROR: |
202 self.__exception = apiproxy_errors.ApplicationError( |
135 self.__exception = apiproxy_errors.ApplicationError( |
208 self.__result_dict['error_detail']) |
141 self.__result_dict['error_detail']) |
209 else: |
142 else: |
210 self.__exception = apiproxy_errors.CapabilityDisabledError( |
143 self.__exception = apiproxy_errors.CapabilityDisabledError( |
211 "The API call %s.%s() is temporarily unavailable." % ( |
144 "The API call %s.%s() is temporarily unavailable." % ( |
212 self.package, self.call)) |
145 self.package, self.call)) |
213 elif _ExceptionsMap.has_key(self.__result_dict['error']): |
146 elif self.__result_dict['error'] in _ExceptionsMap: |
214 exception_entry = _ExceptionsMap[self.__result_dict['error']] |
147 exception_entry = _ExceptionsMap[self.__result_dict['error']] |
215 self.__exception = exception_entry[0]( |
148 self.__exception = exception_entry[0]( |
216 exception_entry[1] % (self.package, self.call)) |
149 exception_entry[1] % (self.package, self.call)) |
217 else: |
150 else: |
218 try: |
151 try: |
219 self.response.ParseFromString(self.__result_dict['result_string']) |
152 self.response.ParseFromString(self.__result_dict['result_string']) |
220 except Exception, e: |
153 except Exception, e: |
221 self.__exception = e |
154 self.__exception = e |
222 if self.callback: |
155 self.__Callback() |
223 try: |
156 |
224 self.callback() |
157 def CreateRPC(): |
225 except: |
158 """Create a RPC instance. suitable for talking to remote services. |
226 exc_class, self.__exception, self.__traceback = sys.exc_info() |
159 |
227 self.__exception._appengine_apiproxy_rpc = self |
160 Each RPC instance can be used only once, and should not be reused. |
228 raise |
161 |
|
162 Returns: |
|
163 an instance of RPC object |
|
164 """ |
|
165 return RPC() |
229 |
166 |
230 |
167 |
231 def MakeSyncCall(package, call, request, response): |
168 def MakeSyncCall(package, call, request, response): |
232 """Makes a synchronous (i.e. blocking) API call within the specified |
169 """Makes a synchronous (i.e. blocking) API call within the specified |
233 package for the specified call method. request and response must be the |
170 package for the specified call method. request and response must be the |