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.
7 from app_yaml_helper
import AppYamlHelper
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())
18 def 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
24 return error
.__class
__.__name
__ == 'DeadlineExceededError'
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.
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
39 from StringIO
import StringIO
41 FAKE_URL_FETCHER_CONFIGURATION
= None
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.
49 global FAKE_URL_FETCHER_CONFIGURATION
50 FAKE_URL_FETCHER_CONFIGURATION
= dict(
51 (re
.compile(k
), v
) for k
, v
in configuration
.iteritems())
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():
60 raise ValueError('No configuration found for %s' % key
)
63 def __init__(self
, result
=None):
72 class FakeUrlFetch(object):
73 """A fake urlfetch module that uses the current
74 |FAKE_URL_FETCHER_CONFIGURATION| to map urls to fake fetchers.
76 class DownloadError(Exception):
79 class _Response(object):
80 def __init__(self
, content
):
81 self
.content
= content
82 self
.headers
= {'Content-Type': 'none'}
83 self
.status_code
= 200
85 def fetch(self
, url
, **kwargs
):
86 url
= url
.split('?', 1)[0]
87 response
= self
._Response
(_GetConfiguration(url
).fetch(url
))
88 if response
.content
is None:
89 response
.status_code
= 404
95 def make_fetch_call(self
, rpc
, url
, **kwargs
):
96 rpc
.result
= self
.fetch(url
)
97 urlfetch
= FakeUrlFetch()
100 class FakeBlobstore(object):
101 class BlobNotFoundError(Exception):
104 class BlobReader(object):
105 def __init__(self
, blob_key
):
106 self
._data
= _BLOBS
[blob_key
].getvalue()
111 blobstore
= FakeBlobstore()
113 class FakeFileInterface(object):
114 """This class allows a StringIO object to be used in a with block like a
117 def __init__(self
, io
):
120 def __exit__(self
, *args
):
123 def write(self
, data
):
126 def __enter__(self
, *args
):
129 class FakeFiles(object):
130 _next_blobstore_key
= 0
131 class blobstore(object):
134 FakeFiles
._next
_blobstore
_key
+= 1
135 return FakeFiles
._next
_blobstore
_key
138 def get_blob_key(filename
):
141 def open(self
, filename
, mode
):
142 _BLOBS
[filename
] = StringIO()
143 return FakeFileInterface(_BLOBS
[filename
])
145 def GetBlobKeys(self
):
148 def finalize(self
, filename
):
153 class Logservice(object):
154 AUTOFLUSH_ENABLED
= True
159 logservice
= Logservice()
161 class InMemoryMemcache(object):
162 """An in-memory memcache implementation.
165 self
._namespaces
= {}
167 class Client(object):
168 def set_multi_async(self
, mapping
, namespace
='', time
=0):
169 for k
, v
in mapping
.iteritems():
170 memcache
.set(k
, v
, namespace
=namespace
, time
=time
)
172 def get_multi_async(self
, keys
, namespace
='', time
=0):
173 return _RPC(result
=dict(
174 (k
, memcache
.get(k
, namespace
=namespace
, time
=time
)) for k
in keys
))
176 def set(self
, key
, value
, namespace
='', time
=0):
177 self
._GetNamespace
(namespace
)[key
] = value
179 def get(self
, key
, namespace
='', time
=0):
180 return self
._GetNamespace
(namespace
).get(key
)
182 def delete(self
, key
, namespace
=''):
183 self
._GetNamespace
(namespace
).pop(key
, None)
185 def delete_multi(self
, keys
, namespace
=''):
187 self
.delete(k
, namespace
=namespace
)
189 def _GetNamespace(self
, namespace
):
190 if namespace
not in self
._namespaces
:
191 self
._namespaces
[namespace
] = {}
192 return self
._namespaces
[namespace
]
194 memcache
= InMemoryMemcache()
196 class webapp2(object):
197 class RequestHandler(object):
198 """A fake webapp2.RequestHandler class for Handler to extend.
200 def __init__(self
, request
, response
):
201 self
.request
= request
202 self
.response
= response
203 self
.response
.status
= 200
205 def redirect(self
, path
, permanent
=False):
206 self
.response
.status
= 301 if permanent
else 302
207 self
.response
.headers
['Location'] = path
209 class _Db_Result(object):
210 def __init__(self
, data
):
213 class _Result(object):
214 def __init__(self
, value
):
218 return self
._Result
(self
._data
)
223 class StringProperty(object):
226 class BlobProperty(object):
230 def __init__(self
, key
):
234 def from_path(model_name
, path
):
235 return db
.Key('%s/%s' % (model_name
, path
))
237 def __eq__(self
, obj
):
238 return self
.__class
__ == obj
.__class
__ and self
._key
== obj
._key
241 return hash(self
._key
)
244 return str(self
._key
)
249 def __init__(self
, **optargs
):
251 for k
, v
in optargs
.iteritems():
252 assert hasattr(cls
, k
), '%s does not define property %s' % (
258 return _Db_Result(db
._store
.get(key
))
261 db
._store
[self
.key_
] = self
.value
265 return _RPC(result
=db
._store
.get(key
))
268 def delete_async(key
):
269 db
._store
.pop(key
, None)
273 def put_async(value
):
274 db
._store
[value
.key
] = value
277 class BlobReferenceProperty(object):