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