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