1.9.30 sync.
[gae.git] / python / google / appengine / api / apiproxy_stub.py
blobf3ef60445710815979eac449bea2834a7e570948
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 """Base class for implementing API proxy stubs."""
29 from __future__ import with_statement
34 import random
35 import threading
37 from google.appengine.api import apiproxy_rpc
38 from google.appengine.api import request_info
39 from google.appengine.runtime import apiproxy_errors
42 MAX_REQUEST_SIZE = 1 << 20
45 class APIProxyStub(object):
46 """Base class for implementing API proxy stub classes.
48 To implement an API proxy stub:
49 - Extend this class.
50 - Override __init__ to pass in appropriate default service name.
51 - Implement service methods as _Dynamic_<method>(request, response).
52 """
56 _ACCEPTS_REQUEST_ID = False
61 THREADSAFE = False
63 def __init__(self, service_name, max_request_size=MAX_REQUEST_SIZE,
64 request_data=None):
65 """Constructor.
67 Args:
68 service_name: Service name expected for all calls.
69 max_request_size: int, maximum allowable size of the incoming request. A
70 apiproxy_errors.RequestTooLargeError will be raised if the inbound
71 request exceeds this size. Default is 1 MB.
72 request_data: A request_info.RequestInfo instance used to look up state
73 associated with the request that generated an API call.
74 """
75 self.__service_name = service_name
76 self.__max_request_size = max_request_size
77 self.request_data = request_data or request_info._local_request_info
81 self._mutex = threading.RLock()
82 self.__error = None
83 self.__error_dict = {}
85 def CreateRPC(self):
86 """Creates RPC object instance.
88 Returns:
89 a instance of RPC.
90 """
91 return apiproxy_rpc.RPC(stub=self)
93 def MakeSyncCall(self, service, call, request, response, request_id=None):
94 """The main RPC entry point.
96 Args:
97 service: Must be name as provided to service_name of constructor.
98 call: A string representing the rpc to make. Must be part of
99 the underlying services methods and impemented by _Dynamic_<call>.
100 request: A protocol buffer of the type corresponding to 'call'.
101 response: A protocol buffer of the type corresponding to 'call'.
102 request_id: A unique string identifying the request associated with the
103 API call.
105 assert service == self.__service_name, ('Expected "%s" service name, '
106 'was "%s"' % (self.__service_name,
107 service))
108 if request.ByteSize() > self.__max_request_size:
109 raise apiproxy_errors.RequestTooLargeError(
110 'The request to API call %s.%s() was too large.' % (service, call))
111 messages = []
112 assert request.IsInitialized(messages), messages
117 exception_type, frequency = self.__error_dict.get(call, (None, None))
118 if exception_type and frequency:
119 if random.random() <= frequency:
120 raise exception_type
122 if self.__error:
123 if random.random() <= self.__error_rate:
124 raise self.__error
127 method = getattr(self, '_Dynamic_' + call)
128 if self._ACCEPTS_REQUEST_ID:
129 method(request, response, request_id)
130 else:
131 method(request, response)
133 def SetError(self, error, method=None, error_rate=1):
134 """Set an error condition that may be raised when calls made to stub.
136 If a method is specified, the error will only apply to that call.
137 The error rate is applied to the method specified or all calls if
138 method is not set.
140 Args:
141 error: An instance of apiproxy_errors.Error or None for no error.
142 method: A string representing the method that the error will affect.
143 error_rate: a number from [0, 1] that sets the chance of the error,
144 defaults to 1.
146 assert error is None or isinstance(error, apiproxy_errors.Error)
147 if method and error:
148 self.__error_dict[method] = error, error_rate
149 else:
150 self.__error_rate = error_rate
151 self.__error = error
154 def Synchronized(method):
155 """Decorator to acquire a mutex around an APIProxyStub method.
157 Args:
158 method: An unbound method of APIProxyStub or a subclass.
160 Returns:
161 The method, altered such it acquires self._mutex throughout its execution.
164 def WrappedMethod(self, *args, **kwargs):
165 with self._mutex:
166 return method(self, *args, **kwargs)
168 return WrappedMethod