appengine_wrappers.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
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# This will attempt to import the actual App Engine modules, and if it fails,
6# they will be replaced with fake modules. This is useful during testing.
7try:
8  import google.appengine.ext.blobstore as blobstore
9  from google.appengine.ext.blobstore.blobstore import BlobReferenceProperty
10  import google.appengine.ext.db as db
11  import google.appengine.ext.webapp as webapp
12  import google.appengine.api.files as files
13  import google.appengine.api.memcache as memcache
14  import google.appengine.api.urlfetch as urlfetch
15  # Default to a 5 minute cache timeout.
16  CACHE_TIMEOUT = 300
17except ImportError:
18  # Cache for one second because zero means cache forever.
19  CACHE_TIMEOUT = 1
20  import re
21  from StringIO import StringIO
22
23  FAKE_URL_FETCHER_CONFIGURATION = None
24
25  def ConfigureFakeUrlFetch(configuration):
26    """|configuration| is a dictionary mapping strings to fake urlfetch classes.
27    A fake urlfetch class just needs to have a fetch method. The keys of the
28    dictionary are treated as regex, and they are matched with the URL to
29    determine which fake urlfetch is used.
30    """
31    global FAKE_URL_FETCHER_CONFIGURATION
32    FAKE_URL_FETCHER_CONFIGURATION = dict(
33        (re.compile(k), v) for k, v in configuration.iteritems())
34
35  def _GetConfiguration(key):
36    if not FAKE_URL_FETCHER_CONFIGURATION:
37      raise ValueError('No fake fetch paths have been configured. '
38                       'See ConfigureFakeUrlFetch in appengine_wrappers.py.')
39    for k, v in FAKE_URL_FETCHER_CONFIGURATION.iteritems():
40      if k.match(key):
41        return v
42    return None
43
44  class _RPC(object):
45    def __init__(self, result=None):
46      self.result = result
47
48    def get_result(self):
49      return self.result
50
51  class FakeUrlFetch(object):
52    """A fake urlfetch module that uses the current
53    |FAKE_URL_FETCHER_CONFIGURATION| to map urls to fake fetchers.
54    """
55    class _Response(object):
56      def __init__(self, content):
57        self.content = content
58        self.headers = { 'content-type': 'none' }
59        self.status_code = 200
60
61    def fetch(self, url, **kwargs):
62      response = self._Response(_GetConfiguration(url).fetch(url))
63      if response.content is None:
64        response.status_code = 404
65      return response
66
67    def create_rpc(self):
68      return _RPC()
69
70    def make_fetch_call(self, rpc, url, **kwargs):
71      rpc.result = self.fetch(url)
72  urlfetch = FakeUrlFetch()
73
74  class NotImplemented(object):
75    def __getattr__(self, attr):
76      raise NotImplementedError()
77
78  _BLOBS = {}
79  class FakeBlobstore(object):
80    class BlobReader(object):
81      def __init__(self, blob_key):
82        self._data = _BLOBS[blob_key].getvalue()
83
84      def read(self):
85        return self._data
86
87  blobstore = FakeBlobstore()
88
89  class FakeFileInterface(object):
90    """This class allows a StringIO object to be used in a with block like a
91    file.
92    """
93    def __init__(self, io):
94      self._io = io
95
96    def __exit__(self, *args):
97      pass
98
99    def write(self, data):
100      self._io.write(data)
101
102    def __enter__(self, *args):
103      return self._io
104
105  class FakeFiles(object):
106    _next_blobstore_key = 0
107    class blobstore(object):
108      @staticmethod
109      def create():
110        FakeFiles._next_blobstore_key += 1
111        return FakeFiles._next_blobstore_key
112
113      @staticmethod
114      def get_blob_key(filename):
115        return filename
116
117    def open(self, filename, mode):
118      _BLOBS[filename] = StringIO()
119      return FakeFileInterface(_BLOBS[filename])
120
121    def GetBlobKeys(self):
122      return _BLOBS.keys()
123
124    def finalize(self, filename):
125      pass
126
127  files = FakeFiles()
128
129  class InMemoryMemcache(object):
130    """A fake memcache that does nothing.
131    """
132    class Client(object):
133      def set_multi_async(self, mapping, namespace='', time=0):
134        return
135
136      def get_multi_async(self, keys, namespace='', time=0):
137        return _RPC(result=dict((k, None) for k in keys))
138
139    def set(self, key, value, namespace='', time=0):
140      return
141
142    def get(self, key, namespace='', time=0):
143      return None
144
145    def delete(self, key, namespace):
146      return
147
148  memcache = InMemoryMemcache()
149
150  class webapp(object):
151    class RequestHandler(object):
152      """A fake webapp.RequestHandler class for Handler to extend.
153      """
154      def __init__(self, request, response):
155        self.request = request
156        self.response = response
157
158      def redirect(self, path):
159        self.request.path = path
160
161  class _Db_Result(object):
162    def __init__(self, data):
163      self._data = data
164
165    class _Result(object):
166      def __init__(self, value):
167        self.value = value
168
169    def get(self):
170      return self._Result(self._data)
171
172  class db(object):
173    _store = {}
174    class StringProperty(object):
175      pass
176
177    class Model(object):
178      def __init__(self, key_='', value=''):
179        self._key = key_
180        self._value = value
181
182      @staticmethod
183      def gql(query, key):
184        return _Db_Result(db._store.get(key, None))
185
186      def put(self):
187        db._store[self._key] = self._value
188
189  class BlobReferenceProperty(object):
190    pass
191