appengine_wrappers.py revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This will attempt to import the actual App Engine modules, and if it fails, 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# they will be replaced with fake modules. This is useful during testing. 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)try: 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import google.appengine.ext.blobstore as blobstore 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from google.appengine.ext.blobstore.blobstore import BlobReferenceProperty 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import google.appengine.ext.db as db 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import google.appengine.ext.webapp as webapp 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import google.appengine.api.files as files 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import google.appengine.api.memcache as memcache 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import google.appengine.api.urlfetch as urlfetch 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Default to a 5 minute cache timeout. 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CACHE_TIMEOUT = 300 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)except ImportError: 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Cache for one second because zero means cache forever. 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CACHE_TIMEOUT = 1 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import re 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) from StringIO import StringIO 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAKE_URL_FETCHER_CONFIGURATION = None 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def ConfigureFakeUrlFetch(configuration): 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """|configuration| is a dictionary mapping strings to fake urlfetch classes. 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A fake urlfetch class just needs to have a fetch method. The keys of the 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dictionary are treated as regex, and they are matched with the URL to 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) determine which fake urlfetch is used. 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global FAKE_URL_FETCHER_CONFIGURATION 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FAKE_URL_FETCHER_CONFIGURATION = dict( 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (re.compile(k), v) for k, v in configuration.iteritems()) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _GetConfiguration(key): 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not FAKE_URL_FETCHER_CONFIGURATION: 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise ValueError('No fake fetch paths have been configured. ' 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'See ConfigureFakeUrlFetch in appengine_wrappers.py.') 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for k, v in FAKE_URL_FETCHER_CONFIGURATION.iteritems(): 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if k.match(key): 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return v 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class _RPC(object): 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, result=None): 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.result = result 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def get_result(self): 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.result 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class FakeUrlFetch(object): 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """A fake urlfetch module that uses the current 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) |FAKE_URL_FETCHER_CONFIGURATION| to map urls to fake fetchers. 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) class DownloadError(Exception): 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pass 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class _Response(object): 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, content): 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.content = content 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.headers = { 'content-type': 'none' } 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.status_code = 200 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def fetch(self, url, **kwargs): 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = self._Response(_GetConfiguration(url).fetch(url)) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if response.content is None: 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response.status_code = 404 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return response 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def create_rpc(self): 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return _RPC() 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def make_fetch_call(self, rpc, url, **kwargs): 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rpc.result = self.fetch(url) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) urlfetch = FakeUrlFetch() 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class NotImplemented(object): 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __getattr__(self, attr): 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise NotImplementedError() 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) _BLOBS = {} 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class FakeBlobstore(object): 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class BlobReader(object): 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, blob_key): 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._data = _BLOBS[blob_key].getvalue() 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def read(self): 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self._data 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) blobstore = FakeBlobstore() 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class FakeFileInterface(object): 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """This class allows a StringIO object to be used in a with block like a 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) file. 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, io): 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._io = io 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __exit__(self, *args): 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def write(self, data): 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._io.write(data) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __enter__(self, *args): 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self._io 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class FakeFiles(object): 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) _next_blobstore_key = 0 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class blobstore(object): 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @staticmethod 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def create(): 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FakeFiles._next_blobstore_key += 1 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FakeFiles._next_blobstore_key 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @staticmethod 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def get_blob_key(filename): 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return filename 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def open(self, filename, mode): 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) _BLOBS[filename] = StringIO() 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FakeFileInterface(_BLOBS[filename]) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetBlobKeys(self): 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return _BLOBS.keys() 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def finalize(self, filename): 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) files = FakeFiles() 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class InMemoryMemcache(object): 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """A fake memcache that does nothing. 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class Client(object): 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def set_multi_async(self, mapping, namespace='', time=0): 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def get_multi_async(self, keys, namespace='', time=0): 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return _RPC(result=dict((k, None) for k in keys)) 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def set(self, key, value, namespace='', time=0): 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def get(self, key, namespace='', time=0): 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def delete(self, key, namespace): 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) memcache = InMemoryMemcache() 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class webapp(object): 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class RequestHandler(object): 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """A fake webapp.RequestHandler class for Handler to extend. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, request, response): 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.request = request 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.response = response 1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.response.status = 200 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def redirect(self, path, permanent=False): 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.response.status = 301 if permanent else 302 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.response.headers['Location'] = path 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class _Db_Result(object): 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, data): 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._data = data 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class _Result(object): 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, value): 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.value = value 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def get(self): 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self._Result(self._data) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class db(object): 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) _store = {} 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class StringProperty(object): 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class Model(object): 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, key_='', value=''): 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._key = key_ 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._value = value 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) @staticmethod 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def gql(query, key): 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return _Db_Result(db._store.get(key, None)) 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def put(self): 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) db._store[self._key] = self._value 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) class BlobReferenceProperty(object): 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 196