moto/moto/core/models.py

160 lines
4.5 KiB
Python
Raw Normal View History

from __future__ import unicode_literals
2013-02-18 21:09:40 +00:00
import functools
import re
from httpretty import HTTPretty
from .responses import metadata_response
2013-03-05 13:14:43 +00:00
from .utils import convert_regex_to_flask_path
2013-02-18 21:09:40 +00:00
2013-02-28 03:25:15 +00:00
class MockAWS(object):
nested_count = 0
def __init__(self, backends):
self.backends = backends
if self.__class__.nested_count == 0:
HTTPretty.reset()
2013-02-28 03:25:15 +00:00
def __call__(self, func):
return self.decorate_callable(func)
def __enter__(self):
self.start()
def __exit__(self, *args):
self.stop()
def start(self):
self.__class__.nested_count += 1
for backend in self.backends.values():
backend.reset()
if not HTTPretty.is_enabled():
HTTPretty.enable()
2013-02-28 03:25:15 +00:00
for method in HTTPretty.METHODS:
2014-08-26 17:25:50 +00:00
backend = list(self.backends.values())[0]
for key, value in backend.urls.items():
2013-02-28 03:25:15 +00:00
HTTPretty.register_uri(
method=method,
uri=re.compile(key),
body=value,
)
# Mock out localhost instance metadata
HTTPretty.register_uri(
method=method,
uri=re.compile('http://169.254.169.254/latest/meta-data/.*'),
body=metadata_response
)
2013-02-28 03:25:15 +00:00
def stop(self):
self.__class__.nested_count -= 1
if self.__class__.nested_count < 0:
raise RuntimeError('Called stop() before start().')
if self.__class__.nested_count == 0:
HTTPretty.disable()
2013-02-28 03:25:15 +00:00
def decorate_callable(self, func):
def wrapper(*args, **kwargs):
with self:
result = func(*args, **kwargs)
return result
functools.update_wrapper(wrapper, func)
wrapper.__wrapped__ = func
2013-02-28 03:25:15 +00:00
return wrapper
class Model(type):
def __new__(self, clsname, bases, namespace):
cls = super(Model, self).__new__(self, clsname, bases, namespace)
cls.__models__ = {}
2014-08-26 17:25:50 +00:00
for name, value in namespace.items():
model = getattr(value, "__returns_model__", False)
if model is not False:
cls.__models__[model] = name
for base in bases:
cls.__models__.update(getattr(base, "__models__", {}))
return cls
@staticmethod
def prop(model_name):
""" decorator to mark a class method as returning model values """
def dec(f):
f.__returns_model__ = model_name
return f
return dec
2013-02-18 21:09:40 +00:00
class BaseBackend(object):
2013-02-18 21:09:40 +00:00
def reset(self):
2013-02-26 04:48:17 +00:00
self.__dict__ = {}
2013-02-20 03:27:36 +00:00
self.__init__()
2013-02-18 21:09:40 +00:00
@property
2013-03-05 13:14:43 +00:00
def _url_module(self):
2013-02-18 21:09:40 +00:00
backend_module = self.__class__.__module__
backend_urls_module_name = backend_module.replace("models", "urls")
2013-03-05 13:14:43 +00:00
backend_urls_module = __import__(backend_urls_module_name, fromlist=['url_bases', 'url_paths'])
return backend_urls_module
@property
def urls(self):
"""
A dictionary of the urls to be mocked with this service and the handlers
that should be called in their place
"""
url_bases = self._url_module.url_bases
unformatted_paths = self._url_module.url_paths
urls = {}
for url_base in url_bases:
2014-08-26 17:25:50 +00:00
for url_path, handler in unformatted_paths.items():
2013-03-05 13:14:43 +00:00
url = url_path.format(url_base)
urls[url] = handler
2013-02-18 21:09:40 +00:00
return urls
2013-03-05 13:14:43 +00:00
@property
def url_paths(self):
"""
A dictionary of the paths of the urls to be mocked with this service and
the handlers that should be called in their place
"""
unformatted_paths = self._url_module.url_paths
paths = {}
2014-08-26 17:25:50 +00:00
for unformatted_path, handler in unformatted_paths.items():
2013-03-05 13:14:43 +00:00
path = unformatted_path.format("")
paths[path] = handler
return paths
@property
def url_bases(self):
"""
A list containing the url_bases extracted from urls.py
"""
return self._url_module.url_bases
2013-03-05 13:14:43 +00:00
@property
def flask_paths(self):
"""
The url paths that will be used for the flask server
"""
paths = {}
2014-08-26 17:25:50 +00:00
for url_path, handler in self.url_paths.items():
2013-03-05 13:14:43 +00:00
url_path = convert_regex_to_flask_path(url_path)
paths[url_path] = handler
return paths
2013-02-28 03:25:15 +00:00
def decorator(self, func=None):
if func:
return MockAWS({'global': self})(func)
2013-02-28 03:25:15 +00:00
else:
return MockAWS({'global': self})