Adding server mode

This commit is contained in:
Steve Pulec 2013-03-05 08:14:43 -05:00
commit a728b2581a
31 changed files with 489 additions and 66 deletions

View file

@ -2,6 +2,7 @@ import functools
import re
from moto.packages.httpretty import HTTPretty
from .utils import convert_regex_to_flask_path
class MockAWS(object):
@ -48,13 +49,56 @@ class BaseBackend(object):
self.__init__()
@property
def urls(self):
def _url_module(self):
backend_module = self.__class__.__module__
backend_urls_module_name = backend_module.replace("models", "urls")
backend_urls_module = __import__(backend_urls_module_name, fromlist=['urls'])
urls = backend_urls_module.urls
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:
for url_path, handler in unformatted_paths.iteritems():
url = url_path.format(url_base)
urls[url] = handler
return urls
@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 = {}
for unformatted_path, handler in unformatted_paths.iteritems():
path = unformatted_path.format("")
paths[path] = handler
return paths
@property
def flask_paths(self):
"""
The url paths that will be used for the flask server
"""
paths = {}
for url_path, handler in self.url_paths.iteritems():
url_path = convert_regex_to_flask_path(url_path)
paths[url_path] = handler
return paths
def decorator(self, func=None):
if func:
return MockAWS(self)(func)

View file

@ -4,6 +4,9 @@ from moto.core.utils import headers_to_dict, camelcase_to_underscores, method_na
class BaseResponse(object):
def dispatch2(self, uri, body, headers):
return self.dispatch(uri, body, headers)
def dispatch(self, uri, body, headers):
if body:
querystring = parse_qs(body)
@ -13,7 +16,7 @@ class BaseResponse(object):
self.path = uri.path
self.querystring = querystring
action = querystring['Action'][0]
action = querystring.get('Action', [""])[0]
action = camelcase_to_underscores(action)
method_names = method_names_from_class(self.__class__)

View file

@ -1,9 +1,17 @@
from collections import namedtuple
import inspect
import random
import re
from urlparse import parse_qs
from flask import request
def headers_to_dict(headers):
if isinstance(headers, dict):
# If already dict, return
return headers
result = {}
for index, header in enumerate(headers.split("\r\n")):
if not header:
@ -51,3 +59,55 @@ def get_random_hex(length=8):
def get_random_message_id():
return '{}-{}-{}-{}-{}'.format(get_random_hex(8), get_random_hex(4), get_random_hex(4), get_random_hex(4), get_random_hex(12))
def convert_regex_to_flask_path(url_path):
"""
Converts a regex matching url to one that can be used with flask
"""
for token in ["$"]:
url_path = url_path.replace(token, "")
def caller(reg):
match_name, match_pattern = reg.groups()
return '<regex("{0}"):{1}>'.format(match_pattern, match_name)
url_path = re.sub("\(\?P<(.*?)>(.*?)\)", caller, url_path)
return url_path
class convert_flask_to_httpretty_response(object):
def __init__(self, callback):
self.callback = callback
@property
def __name__(self):
# For instance methods, use class and method names. Otherwise
# use module and method name
if inspect.ismethod(self.callback):
outer = self.callback.im_class.__name__
else:
outer = self.callback.__module__
return "{}.{}".format(outer, self.callback.__name__)
def __call__(self, args=None, **kwargs):
hostname = request.host_url
method = request.method
path = request.path
query = request.query_string
# Mimic the HTTPretty URIInfo class
URI = namedtuple('URI', 'hostname method path query')
uri = URI(hostname, method, path, query)
body = request.data or query
headers = dict(request.headers)
result = self.callback(uri, body, headers)
if isinstance(result, basestring):
# result is just the response
return result
else:
# result is a responce, headers tuple
response, headers = result
status = headers.pop('status', None)
return response, status, headers