1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 def IsDeadlineExceededError(error):
6 '''A general way of determining whether |error| is a DeadlineExceededError,
7 since there are 3 different types thrown by AppEngine and we might as well
8 handle them all the same way. For more info see:
9 https://developers.google.com/appengine/articles/deadlineexceedederrors
11 return type(error).__name__ == 'DeadlineExceededError'
14 def IsDownloadError(error):
15 return type(error).__name__ == 'DownloadError'
18 # This will attempt to import the actual App Engine modules, and if it fails,
19 # they will be replaced with fake modules. This is useful during testing.
21 import google.appengine.api.app_identity as app_identity
22 import google.appengine.api.files as files
23 import google.appengine.api.logservice as logservice
24 import google.appengine.api.memcache as memcache
25 import google.appengine.api.taskqueue as taskqueue
26 import google.appengine.api.urlfetch as urlfetch
27 import google.appengine.ext.blobstore as blobstore
28 from google.appengine.ext.blobstore.blobstore import BlobReferenceProperty
29 import google.appengine.ext.db as db
33 from StringIO import StringIO
35 FAKE_URL_FETCHER_CONFIGURATION = None
37 def ConfigureFakeUrlFetch(configuration):
38 """|configuration| is a dictionary mapping strings to fake urlfetch classes.
39 A fake urlfetch class just needs to have a fetch method. The keys of the
40 dictionary are treated as regex, and they are matched with the URL to
41 determine which fake urlfetch is used.
43 global FAKE_URL_FETCHER_CONFIGURATION
44 FAKE_URL_FETCHER_CONFIGURATION = dict(
45 (re.compile(k), v) for k, v in configuration.iteritems())
47 def _GetConfiguration(key):
48 if not FAKE_URL_FETCHER_CONFIGURATION:
49 raise ValueError('No fake fetch paths have been configured. '
50 'See ConfigureFakeUrlFetch in appengine_wrappers.py.')
51 for k, v in FAKE_URL_FETCHER_CONFIGURATION.iteritems():
54 raise ValueError('No configuration found for %s' % key)
57 def __init__(self, result=None):
66 class FakeAppIdentity(object):
67 """A fake app_identity module that returns no access tokens."""
68 def get_access_token(self, scope):
70 app_identity = FakeAppIdentity()
72 class FakeUrlFetch(object):
73 """A fake urlfetch module that uses the current
74 |FAKE_URL_FETCHER_CONFIGURATION| to map urls to fake fetchers.
76 class DownloadError(Exception):
79 class _Response(object):
80 def __init__(self, content):
81 self.content = content
82 self.headers = {'Content-Type': 'none'}
83 self.status_code = 200
85 def fetch(self, url, **kwargs):
86 url = url.split('?', 1)[0]
87 response = self._Response(_GetConfiguration(url).fetch(url))
88 if response.content is None:
89 response.status_code = 404
92 def create_rpc(self, **kwargs):
95 def make_fetch_call(self, rpc, url, **kwargs):
96 rpc.result = self.fetch(url)
97 urlfetch = FakeUrlFetch()
100 class FakeBlobstore(object):
101 class BlobNotFoundError(Exception):
104 class BlobReader(object):
105 def __init__(self, blob_key):
106 self._data = _BLOBS[blob_key].getvalue()
111 blobstore = FakeBlobstore()
113 class FakeFileInterface(object):
114 """This class allows a StringIO object to be used in a with block like a
117 def __init__(self, io):
120 def __exit__(self, *args):
123 def write(self, data):
126 def __enter__(self, *args):
129 class FakeFiles(object):
130 _next_blobstore_key = 0
131 class blobstore(object):
134 FakeFiles._next_blobstore_key += 1
135 return FakeFiles._next_blobstore_key
138 def get_blob_key(filename):
141 def open(self, filename, mode):
142 _BLOBS[filename] = StringIO()
143 return FakeFileInterface(_BLOBS[filename])
145 def GetBlobKeys(self):
148 def finalize(self, filename):
153 class Logservice(object):
154 AUTOFLUSH_ENABLED = True
159 logservice = Logservice()
161 class InMemoryMemcache(object):
162 """An in-memory memcache implementation.
165 self._namespaces = {}
167 class Client(object):
168 def set_multi_async(self, mapping, namespace='', time=0):
169 for k, v in mapping.iteritems():
170 memcache.set(k, v, namespace=namespace, time=time)
172 def get_multi_async(self, keys, namespace='', time=0):
173 return _RPC(result=dict(
174 (k, memcache.get(k, namespace=namespace, time=time)) for k in keys))
176 def set(self, key, value, namespace='', time=0):
177 self._GetNamespace(namespace)[key] = value
179 def get(self, key, namespace='', time=0):
180 return self._GetNamespace(namespace).get(key)
182 def delete(self, key, namespace=''):
183 self._GetNamespace(namespace).pop(key, None)
185 def delete_multi(self, keys, namespace=''):
187 self.delete(k, namespace=namespace)
189 def _GetNamespace(self, namespace):
190 if namespace not in self._namespaces:
191 self._namespaces[namespace] = {}
192 return self._namespaces[namespace]
194 memcache = InMemoryMemcache()
196 class webapp2(object):
197 class RequestHandler(object):
198 """A fake webapp2.RequestHandler class for Handler to extend.
200 def __init__(self, request, response):
201 self.request = request
202 self.response = response
203 self.response.status = 200
205 def redirect(self, path, permanent=False):
206 self.response.status = 301 if permanent else 302
207 self.response.headers['Location'] = path
209 class _Db_Result(object):
210 def __init__(self, data):
213 class _Result(object):
214 def __init__(self, value):
218 return self._Result(self._data)
223 class StringProperty(object):
226 class BlobProperty(object):
230 def __init__(self, key):
234 def from_path(model_name, path):
235 return db.Key('%s/%s' % (model_name, path))
237 def __eq__(self, obj):
238 return self.__class__ == obj.__class__ and self._key == obj._key
241 return hash(self._key)
244 return str(self._key)
249 def __init__(self, **optargs):
251 for k, v in optargs.iteritems():
252 assert hasattr(cls, k), '%s does not define property %s' % (
258 return _Db_Result(db._store.get(key))
261 db._store[self.key_] = self.value
265 return _RPC(result=db._store.get(key))
268 def delete_async(key):
269 db._store.pop(key, None)
273 def put_async(value):
274 db._store[value.key] = value
277 class BlobReferenceProperty(object):
280 # Executes any queued tasks synchronously as they are queued.
283 def SetTaskRunnerForTest(task_runner):
285 _task_runner = task_runner
287 class SynchronousTaskQueue(object):
289 def __init__(self, url=None, params={}):
291 self.params_ = params
297 return self.params_.get('commit')
300 def __init__(self, name='default'):
306 _task_runner(task.GetUrl(), task.GetCommit())
312 taskqueue = SynchronousTaskQueue()