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 type(error
).__name
__ == 'DeadlineExceededError'
26 def IsDownloadError(error
):
27 return type(error
).__name
__ == 'DownloadError'
29 # This will attempt to import the actual App Engine modules, and if it fails,
30 # they will be replaced with fake modules. This is useful during testing.
32 import google
.appengine
.api
.files
as files
33 import google
.appengine
.api
.logservice
as logservice
34 import google
.appengine
.api
.memcache
as memcache
35 import google
.appengine
.api
.urlfetch
as urlfetch
36 import google
.appengine
.ext
.blobstore
as blobstore
37 from google
.appengine
.ext
.blobstore
.blobstore
import BlobReferenceProperty
38 import google
.appengine
.ext
.db
as db
42 from StringIO
import StringIO
44 FAKE_URL_FETCHER_CONFIGURATION
= None
46 def ConfigureFakeUrlFetch(configuration
):
47 """|configuration| is a dictionary mapping strings to fake urlfetch classes.
48 A fake urlfetch class just needs to have a fetch method. The keys of the
49 dictionary are treated as regex, and they are matched with the URL to
50 determine which fake urlfetch is used.
52 global FAKE_URL_FETCHER_CONFIGURATION
53 FAKE_URL_FETCHER_CONFIGURATION
= dict(
54 (re
.compile(k
), v
) for k
, v
in configuration
.iteritems())
56 def _GetConfiguration(key
):
57 if not FAKE_URL_FETCHER_CONFIGURATION
:
58 raise ValueError('No fake fetch paths have been configured. '
59 'See ConfigureFakeUrlFetch in appengine_wrappers.py.')
60 for k
, v
in FAKE_URL_FETCHER_CONFIGURATION
.iteritems():
63 raise ValueError('No configuration found for %s' % key
)
66 def __init__(self
, result
=None):
75 class FakeUrlFetch(object):
76 """A fake urlfetch module that uses the current
77 |FAKE_URL_FETCHER_CONFIGURATION| to map urls to fake fetchers.
79 class DownloadError(Exception):
82 class _Response(object):
83 def __init__(self
, content
):
84 self
.content
= content
85 self
.headers
= {'Content-Type': 'none'}
86 self
.status_code
= 200
88 def fetch(self
, url
, **kwargs
):
89 url
= url
.split('?', 1)[0]
90 response
= self
._Response
(_GetConfiguration(url
).fetch(url
))
91 if response
.content
is None:
92 response
.status_code
= 404
98 def make_fetch_call(self
, rpc
, url
, **kwargs
):
99 rpc
.result
= self
.fetch(url
)
100 urlfetch
= FakeUrlFetch()
103 class FakeBlobstore(object):
104 class BlobNotFoundError(Exception):
107 class BlobReader(object):
108 def __init__(self
, blob_key
):
109 self
._data
= _BLOBS
[blob_key
].getvalue()
114 blobstore
= FakeBlobstore()
116 class FakeFileInterface(object):
117 """This class allows a StringIO object to be used in a with block like a
120 def __init__(self
, io
):
123 def __exit__(self
, *args
):
126 def write(self
, data
):
129 def __enter__(self
, *args
):
132 class FakeFiles(object):
133 _next_blobstore_key
= 0
134 class blobstore(object):
137 FakeFiles
._next
_blobstore
_key
+= 1
138 return FakeFiles
._next
_blobstore
_key
141 def get_blob_key(filename
):
144 def open(self
, filename
, mode
):
145 _BLOBS
[filename
] = StringIO()
146 return FakeFileInterface(_BLOBS
[filename
])
148 def GetBlobKeys(self
):
151 def finalize(self
, filename
):
156 class Logservice(object):
157 AUTOFLUSH_ENABLED
= True
162 logservice
= Logservice()
164 class InMemoryMemcache(object):
165 """An in-memory memcache implementation.
168 self
._namespaces
= {}
170 class Client(object):
171 def set_multi_async(self
, mapping
, namespace
='', time
=0):
172 for k
, v
in mapping
.iteritems():
173 memcache
.set(k
, v
, namespace
=namespace
, time
=time
)
175 def get_multi_async(self
, keys
, namespace
='', time
=0):
176 return _RPC(result
=dict(
177 (k
, memcache
.get(k
, namespace
=namespace
, time
=time
)) for k
in keys
))
179 def set(self
, key
, value
, namespace
='', time
=0):
180 self
._GetNamespace
(namespace
)[key
] = value
182 def get(self
, key
, namespace
='', time
=0):
183 return self
._GetNamespace
(namespace
).get(key
)
185 def delete(self
, key
, namespace
=''):
186 self
._GetNamespace
(namespace
).pop(key
, None)
188 def delete_multi(self
, keys
, namespace
=''):
190 self
.delete(k
, namespace
=namespace
)
192 def _GetNamespace(self
, namespace
):
193 if namespace
not in self
._namespaces
:
194 self
._namespaces
[namespace
] = {}
195 return self
._namespaces
[namespace
]
197 memcache
= InMemoryMemcache()
199 class webapp2(object):
200 class RequestHandler(object):
201 """A fake webapp2.RequestHandler class for Handler to extend.
203 def __init__(self
, request
, response
):
204 self
.request
= request
205 self
.response
= response
206 self
.response
.status
= 200
208 def redirect(self
, path
, permanent
=False):
209 self
.response
.status
= 301 if permanent
else 302
210 self
.response
.headers
['Location'] = path
212 class _Db_Result(object):
213 def __init__(self
, data
):
216 class _Result(object):
217 def __init__(self
, value
):
221 return self
._Result
(self
._data
)
226 class StringProperty(object):
229 class BlobProperty(object):
233 def __init__(self
, key
):
237 def from_path(model_name
, path
):
238 return db
.Key('%s/%s' % (model_name
, path
))
240 def __eq__(self
, obj
):
241 return self
.__class
__ == obj
.__class
__ and self
._key
== obj
._key
244 return hash(self
._key
)
247 return str(self
._key
)
252 def __init__(self
, **optargs
):
254 for k
, v
in optargs
.iteritems():
255 assert hasattr(cls
, k
), '%s does not define property %s' % (
261 return _Db_Result(db
._store
.get(key
))
264 db
._store
[self
.key_
] = self
.value
268 return _RPC(result
=db
._store
.get(key
))
271 def delete_async(key
):
272 db
._store
.pop(key
, None)
276 def put_async(value
):
277 db
._store
[value
.key
] = value
280 class BlobReferenceProperty(object):