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. 4 5import os 6 7from app_yaml_helper import AppYamlHelper 8 9def GetAppVersion(): 10 if 'CURRENT_VERSION_ID' in os.environ: 11 # The version ID looks like 2-0-25.36712548, we only want the 2-0-25. 12 return os.environ['CURRENT_VERSION_ID'].split('.', 1)[0] 13 # Not running on appengine, get it from the app.yaml file ourselves. 14 app_yaml_path = os.path.join(os.path.split(__file__)[0], 'app.yaml') 15 with open(app_yaml_path, 'r') as app_yaml: 16 return AppYamlHelper.ExtractVersion(app_yaml.read()) 17 18def IsDeadlineExceededError(error): 19 '''A general way of determining whether |error| is a DeadlineExceededError, 20 since there are 3 different types thrown by AppEngine and we might as well 21 handle them all the same way. For more info see: 22 https://developers.google.com/appengine/articles/deadlineexceedederrors 23 ''' 24 return error.__class__.__name__ == 'DeadlineExceededError' 25 26# This will attempt to import the actual App Engine modules, and if it fails, 27# they will be replaced with fake modules. This is useful during testing. 28try: 29 import google.appengine.api.files as files 30 import google.appengine.api.logservice as logservice 31 import google.appengine.api.memcache as memcache 32 import google.appengine.api.urlfetch as urlfetch 33 import google.appengine.ext.blobstore as blobstore 34 from google.appengine.ext.blobstore.blobstore import BlobReferenceProperty 35 import google.appengine.ext.db as db 36 import webapp2 37except ImportError: 38 import re 39 from StringIO import StringIO 40 41 FAKE_URL_FETCHER_CONFIGURATION = None 42 43 def ConfigureFakeUrlFetch(configuration): 44 """|configuration| is a dictionary mapping strings to fake urlfetch classes. 45 A fake urlfetch class just needs to have a fetch method. The keys of the 46 dictionary are treated as regex, and they are matched with the URL to 47 determine which fake urlfetch is used. 48 """ 49 global FAKE_URL_FETCHER_CONFIGURATION 50 FAKE_URL_FETCHER_CONFIGURATION = dict( 51 (re.compile(k), v) for k, v in configuration.iteritems()) 52 53 def _GetConfiguration(key): 54 if not FAKE_URL_FETCHER_CONFIGURATION: 55 raise ValueError('No fake fetch paths have been configured. ' 56 'See ConfigureFakeUrlFetch in appengine_wrappers.py.') 57 for k, v in FAKE_URL_FETCHER_CONFIGURATION.iteritems(): 58 if k.match(key): 59 return v 60 return None 61 62 class _RPC(object): 63 def __init__(self, result=None): 64 self.result = result 65 66 def get_result(self): 67 return self.result 68 69 def wait(self): 70 pass 71 72 class FakeUrlFetch(object): 73 """A fake urlfetch module that uses the current 74 |FAKE_URL_FETCHER_CONFIGURATION| to map urls to fake fetchers. 75 """ 76 class DownloadError(Exception): 77 pass 78 79 class _Response(object): 80 def __init__(self, content): 81 self.content = content 82 self.headers = {'Content-Type': 'none'} 83 self.status_code = 200 84 85 def fetch(self, url, **kwargs): 86 response = self._Response(_GetConfiguration(url).fetch(url)) 87 if response.content is None: 88 response.status_code = 404 89 return response 90 91 def create_rpc(self): 92 return _RPC() 93 94 def make_fetch_call(self, rpc, url, **kwargs): 95 rpc.result = self.fetch(url) 96 urlfetch = FakeUrlFetch() 97 98 _BLOBS = {} 99 class FakeBlobstore(object): 100 class BlobReader(object): 101 def __init__(self, blob_key): 102 self._data = _BLOBS[blob_key].getvalue() 103 104 def read(self): 105 return self._data 106 107 blobstore = FakeBlobstore() 108 109 class FakeFileInterface(object): 110 """This class allows a StringIO object to be used in a with block like a 111 file. 112 """ 113 def __init__(self, io): 114 self._io = io 115 116 def __exit__(self, *args): 117 pass 118 119 def write(self, data): 120 self._io.write(data) 121 122 def __enter__(self, *args): 123 return self._io 124 125 class FakeFiles(object): 126 _next_blobstore_key = 0 127 class blobstore(object): 128 @staticmethod 129 def create(): 130 FakeFiles._next_blobstore_key += 1 131 return FakeFiles._next_blobstore_key 132 133 @staticmethod 134 def get_blob_key(filename): 135 return filename 136 137 def open(self, filename, mode): 138 _BLOBS[filename] = StringIO() 139 return FakeFileInterface(_BLOBS[filename]) 140 141 def GetBlobKeys(self): 142 return _BLOBS.keys() 143 144 def finalize(self, filename): 145 pass 146 147 files = FakeFiles() 148 149 class Logservice(object): 150 AUTOFLUSH_ENABLED = True 151 152 def flush(self): 153 pass 154 155 logservice = Logservice() 156 157 class InMemoryMemcache(object): 158 """An in-memory memcache implementation. 159 """ 160 def __init__(self): 161 self._namespaces = {} 162 163 class Client(object): 164 def set_multi_async(self, mapping, namespace='', time=0): 165 for k, v in mapping.iteritems(): 166 memcache.set(k, v, namespace=namespace, time=time) 167 168 def get_multi_async(self, keys, namespace='', time=0): 169 return _RPC(result=dict( 170 (k, memcache.get(k, namespace=namespace, time=time)) for k in keys)) 171 172 def set(self, key, value, namespace='', time=0): 173 self._GetNamespace(namespace)[key] = value 174 175 def get(self, key, namespace='', time=0): 176 return self._GetNamespace(namespace).get(key) 177 178 def delete(self, key, namespace=''): 179 self._GetNamespace(namespace).pop(key, None) 180 181 def delete_multi(self, keys, namespace=''): 182 for k in keys: 183 self.delete(k, namespace=namespace) 184 185 def _GetNamespace(self, namespace): 186 if namespace not in self._namespaces: 187 self._namespaces[namespace] = {} 188 return self._namespaces[namespace] 189 190 memcache = InMemoryMemcache() 191 192 class webapp2(object): 193 class RequestHandler(object): 194 """A fake webapp2.RequestHandler class for Handler to extend. 195 """ 196 def __init__(self, request, response): 197 self.request = request 198 self.response = response 199 self.response.status = 200 200 201 def redirect(self, path, permanent=False): 202 self.response.status = 301 if permanent else 302 203 self.response.headers['Location'] = path 204 205 class _Db_Result(object): 206 def __init__(self, data): 207 self._data = data 208 209 class _Result(object): 210 def __init__(self, value): 211 self.value = value 212 213 def get(self): 214 return self._Result(self._data) 215 216 class db(object): 217 _store = {} 218 219 class StringProperty(object): 220 pass 221 222 class BlobProperty(object): 223 pass 224 225 class Key(object): 226 def __init__(self, key): 227 self._key = key 228 229 @staticmethod 230 def from_path(model_name, path): 231 return db.Key('%s/%s' % (model_name, path)) 232 233 def __eq__(self, obj): 234 return self.__class__ == obj.__class__ and self._key == obj._key 235 236 def __hash__(self): 237 return hash(self._key) 238 239 def __str__(self): 240 return str(self._key) 241 242 class Model(object): 243 key = None 244 245 def __init__(self, **optargs): 246 cls = self.__class__ 247 for k, v in optargs.iteritems(): 248 assert hasattr(cls, k), '%s does not define property %s' % ( 249 cls.__name__, k) 250 setattr(self, k, v) 251 252 @staticmethod 253 def gql(query, key): 254 return _Db_Result(db._store.get(key)) 255 256 def put(self): 257 db._store[self.key_] = self.value 258 259 @staticmethod 260 def get_async(key): 261 return _RPC(result=db._store.get(key)) 262 263 @staticmethod 264 def delete_async(key): 265 db._store.pop(key, None) 266 return _RPC() 267 268 @staticmethod 269 def put_async(value): 270 db._store[value.key] = value 271 return _RPC() 272 273 class BlobReferenceProperty(object): 274 pass 275