move back to bundled httpretty for now
This commit is contained in:
parent
46b38c705c
commit
fe2b3518ae
@ -1,7 +1,7 @@
|
|||||||
import functools
|
import functools
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from httpretty import HTTPretty
|
from moto.packages.httpretty import HTTPretty
|
||||||
from .responses import metadata_response
|
from .responses import metadata_response
|
||||||
from .utils import convert_regex_to_flask_path
|
from .utils import convert_regex_to_flask_path
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# #!/usr/bin/env python
|
# #!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# <HTTPretty - HTTP client mock for Python>
|
# <HTTPretty - HTTP client mock for Python>
|
||||||
# Copyright (C) <2011-2012> Gabriel Falcão <gabriel@nacaolivre.org>
|
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person
|
# Permission is hereby granted, free of charge, to any person
|
||||||
# obtaining a copy of this software and associated documentation
|
# obtaining a copy of this software and associated documentation
|
||||||
@ -23,7 +23,9 @@
|
|||||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
# OTHER DEALINGS IN THE SOFTWARE.
|
# OTHER DEALINGS IN THE SOFTWARE.
|
||||||
version = '0.5.8'
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
version = '0.5.12'
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import inspect
|
import inspect
|
||||||
@ -40,8 +42,10 @@ PY3 = sys.version_info[0] == 3
|
|||||||
if PY3:
|
if PY3:
|
||||||
text_type = str
|
text_type = str
|
||||||
byte_type = bytes
|
byte_type = bytes
|
||||||
|
basestring = (str, bytes)
|
||||||
|
|
||||||
import io
|
import io
|
||||||
StringIO = io.StringIO
|
StringIO = io.BytesIO
|
||||||
|
|
||||||
class Py3kObject(object):
|
class Py3kObject(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -64,9 +68,10 @@ class Py3kObject(object):
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
try:
|
try:
|
||||||
from urllib.parse import urlsplit, parse_qs
|
from urllib.parse import urlsplit, urlunsplit, parse_qs, quote, quote_plus
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from urlparse import urlsplit, parse_qs
|
from urlparse import urlsplit, urlunsplit, parse_qs
|
||||||
|
from urllib import quote, quote_plus
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from http.server import BaseHTTPRequestHandler
|
from http.server import BaseHTTPRequestHandler
|
||||||
@ -99,6 +104,14 @@ except ImportError:
|
|||||||
ssl = None
|
ssl = None
|
||||||
|
|
||||||
|
|
||||||
|
ClassTypes = (type,)
|
||||||
|
if not PY3:
|
||||||
|
ClassTypes = (type, types.ClassType)
|
||||||
|
|
||||||
|
|
||||||
|
POTENTIAL_HTTP_PORTS = [80, 443]
|
||||||
|
|
||||||
|
|
||||||
class HTTPrettyError(Exception):
|
class HTTPrettyError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -110,6 +123,13 @@ def utf8(s):
|
|||||||
return byte_type(s)
|
return byte_type(s)
|
||||||
|
|
||||||
|
|
||||||
|
def decode_utf8(s):
|
||||||
|
if isinstance(s, byte_type):
|
||||||
|
s = s.decode("utf-8")
|
||||||
|
|
||||||
|
return text_type(s)
|
||||||
|
|
||||||
|
|
||||||
def parse_requestline(s):
|
def parse_requestline(s):
|
||||||
"""
|
"""
|
||||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5
|
http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5
|
||||||
@ -123,8 +143,8 @@ def parse_requestline(s):
|
|||||||
...
|
...
|
||||||
ValueError: Not a Request-Line
|
ValueError: Not a Request-Line
|
||||||
"""
|
"""
|
||||||
methods = '|'.join(HTTPretty.METHODS)
|
methods = b'|'.join(HTTPretty.METHODS)
|
||||||
m = re.match(r'('+methods+')\s+(.*)\s+HTTP/(1.[0|1])', s, re.I)
|
m = re.match(br'(' + methods + b')\s+(.*)\s+HTTP/(1.[0|1])', s, re.I)
|
||||||
if m:
|
if m:
|
||||||
return m.group(1).upper(), m.group(2), m.group(3)
|
return m.group(1).upper(), m.group(2), m.group(3)
|
||||||
else:
|
else:
|
||||||
@ -135,7 +155,9 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, Py3kObject):
|
|||||||
def __init__(self, headers, body=''):
|
def __init__(self, headers, body=''):
|
||||||
self.body = utf8(body)
|
self.body = utf8(body)
|
||||||
self.raw_headers = utf8(headers)
|
self.raw_headers = utf8(headers)
|
||||||
self.rfile = StringIO('\r\n\r\n'.join([headers.strip(), body]))
|
self.client_address = ['10.0.0.1']
|
||||||
|
self.rfile = StringIO(b'\r\n\r\n'.join([headers.strip(), body]))
|
||||||
|
self.wfile = StringIO()
|
||||||
self.raw_requestline = self.rfile.readline()
|
self.raw_requestline = self.rfile.readline()
|
||||||
self.error_code = self.error_message = None
|
self.error_code = self.error_message = None
|
||||||
self.parse_request()
|
self.parse_request()
|
||||||
@ -159,15 +181,7 @@ class HTTPrettyRequestEmpty(object):
|
|||||||
|
|
||||||
|
|
||||||
class FakeSockFile(StringIO):
|
class FakeSockFile(StringIO):
|
||||||
def read(self, amount=None):
|
pass
|
||||||
amount = amount or self.len
|
|
||||||
new_amount = amount
|
|
||||||
|
|
||||||
if amount > self.len:
|
|
||||||
new_amount = self.len - self.tell()
|
|
||||||
|
|
||||||
ret = StringIO.read(self, new_amount)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
class FakeSSLSocket(object):
|
class FakeSSLSocket(object):
|
||||||
@ -194,6 +208,7 @@ class fakesock(object):
|
|||||||
self.fd = FakeSockFile()
|
self.fd = FakeSockFile()
|
||||||
self.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
|
self.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
|
||||||
self._sock = self
|
self._sock = self
|
||||||
|
self.is_http = False
|
||||||
|
|
||||||
def getpeercert(self, *a, **kw):
|
def getpeercert(self, *a, **kw):
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
@ -230,6 +245,9 @@ class fakesock(object):
|
|||||||
def connect(self, address):
|
def connect(self, address):
|
||||||
self._address = (self._host, self._port) = address
|
self._address = (self._host, self._port) = address
|
||||||
self._closed = False
|
self._closed = False
|
||||||
|
self.is_http = self._port in POTENTIAL_HTTP_PORTS
|
||||||
|
if not self.is_http:
|
||||||
|
self.truesock.connect(self._address)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if not self._closed:
|
if not self._closed:
|
||||||
@ -246,17 +264,22 @@ class fakesock(object):
|
|||||||
return self.fd
|
return self.fd
|
||||||
|
|
||||||
def _true_sendall(self, data, *args, **kw):
|
def _true_sendall(self, data, *args, **kw):
|
||||||
self.truesock.connect(self._address)
|
if self.is_http:
|
||||||
|
self.truesock.connect(self._address)
|
||||||
|
|
||||||
self.truesock.sendall(data, *args, **kw)
|
self.truesock.sendall(data, *args, **kw)
|
||||||
_d = self.truesock.recv(16)
|
|
||||||
self.fd.seek(0)
|
_d = True
|
||||||
self.fd.write(_d)
|
|
||||||
while _d:
|
while _d:
|
||||||
_d = self.truesock.recv(16)
|
try:
|
||||||
self.fd.write(_d)
|
_d = self.truesock.recv(16)
|
||||||
|
self.truesock.settimeout(0.0)
|
||||||
|
self.fd.write(_d)
|
||||||
|
|
||||||
|
except socket.error:
|
||||||
|
break
|
||||||
|
|
||||||
self.fd.seek(0)
|
self.fd.seek(0)
|
||||||
self.truesock.close()
|
|
||||||
|
|
||||||
def sendall(self, data, *args, **kw):
|
def sendall(self, data, *args, **kw):
|
||||||
|
|
||||||
@ -264,22 +287,13 @@ class fakesock(object):
|
|||||||
hostnames = [getattr(i.info, 'hostname', None) for i in HTTPretty._entries.keys()]
|
hostnames = [getattr(i.info, 'hostname', None) for i in HTTPretty._entries.keys()]
|
||||||
self.fd.seek(0)
|
self.fd.seek(0)
|
||||||
try:
|
try:
|
||||||
print("data", data)
|
requestline, _ = data.split(b'\r\n', 1)
|
||||||
requestline, _ = data.split('\r\n', 1)
|
|
||||||
method, path, version = parse_requestline(requestline)
|
method, path, version = parse_requestline(requestline)
|
||||||
is_parsing_headers = True
|
is_parsing_headers = True
|
||||||
except ValueError:
|
except ValueError:
|
||||||
is_parsing_headers = False
|
is_parsing_headers = False
|
||||||
|
|
||||||
# This need to be reconsidered. URIMatchers with regexs don't
|
|
||||||
# have hostnames which can cause this to return even though
|
|
||||||
# the regex may have matched
|
|
||||||
# if self._host not in hostnames:
|
|
||||||
# return self._true_sendall(data)
|
|
||||||
|
|
||||||
import pdb;pdb.set_trace()
|
|
||||||
if not is_parsing_headers:
|
if not is_parsing_headers:
|
||||||
|
|
||||||
if len(self._sent_data) > 1:
|
if len(self._sent_data) > 1:
|
||||||
headers, body = map(utf8, self._sent_data[-2:])
|
headers, body = map(utf8, self._sent_data[-2:])
|
||||||
|
|
||||||
@ -288,8 +302,7 @@ class fakesock(object):
|
|||||||
|
|
||||||
info = URIInfo(hostname=self._host, port=self._port,
|
info = URIInfo(hostname=self._host, port=self._port,
|
||||||
path=split_url.path,
|
path=split_url.path,
|
||||||
query=split_url.query,
|
query=split_url.query)
|
||||||
method=method)
|
|
||||||
|
|
||||||
# If we are sending more data to a dynamic response entry,
|
# If we are sending more data to a dynamic response entry,
|
||||||
# we need to call the method again.
|
# we need to call the method again.
|
||||||
@ -298,41 +311,36 @@ class fakesock(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return HTTPretty.historify_request(headers, body, False)
|
return HTTPretty.historify_request(headers, body, False)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(traceback.format_exc(e))
|
logging.error(traceback.format_exc(e))
|
||||||
return self._true_sendall(data, *args, **kw)
|
return self._true_sendall(data, *args, **kw)
|
||||||
|
|
||||||
# path might come with
|
# path might come with
|
||||||
s = urlsplit(path)
|
s = urlsplit(path)
|
||||||
|
POTENTIAL_HTTP_PORTS.append(int(s.port or 80))
|
||||||
headers, body = map(utf8, data.split('\r\n\r\n', 1))
|
headers, body = map(utf8, data.split(b'\r\n\r\n', 1))
|
||||||
|
|
||||||
request = HTTPretty.historify_request(headers, body)
|
request = HTTPretty.historify_request(headers, body)
|
||||||
|
|
||||||
info = URIInfo(hostname=self._host, port=self._port,
|
info = URIInfo(hostname=self._host, port=self._port,
|
||||||
path=s.path,
|
path=s.path,
|
||||||
query=s.query,
|
query=s.query,
|
||||||
last_request=request,
|
last_request=request)
|
||||||
method=method)
|
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
for matcher, value in HTTPretty._entries.items():
|
for matcher, value in HTTPretty._entries.items():
|
||||||
if matcher.matches(info) and matcher.method == method:
|
if matcher.matches(info):
|
||||||
entries = value
|
entries = value
|
||||||
#info = matcher.info
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if not entries:
|
if not entries:
|
||||||
self._true_sendall(data)
|
self._true_sendall(data)
|
||||||
return
|
return
|
||||||
|
|
||||||
entry = matcher.get_next_entry()
|
self._entry = matcher.get_next_entry(method)
|
||||||
if entry.method == method:
|
self._request = (info, body, headers)
|
||||||
self._entry = entry
|
|
||||||
self._request = (info, method, body, headers)
|
|
||||||
else:
|
|
||||||
raise ValueError("No match found for", method, entry.uri)
|
|
||||||
|
|
||||||
def debug(*a, **kw):
|
def debug(*a, **kw):
|
||||||
frame = inspect.stack()[0][0]
|
frame = inspect.stack()[0][0]
|
||||||
@ -343,7 +351,7 @@ class fakesock(object):
|
|||||||
("Please open an issue at "
|
("Please open an issue at "
|
||||||
"'https://github.com/gabrielfalcao/HTTPretty/issues'"),
|
"'https://github.com/gabrielfalcao/HTTPretty/issues'"),
|
||||||
"And paste the following traceback:\n",
|
"And paste the following traceback:\n",
|
||||||
"".join(lines),
|
"".join(decode_utf8(lines)),
|
||||||
]
|
]
|
||||||
raise RuntimeError("\n".join(message))
|
raise RuntimeError("\n".join(message))
|
||||||
|
|
||||||
@ -527,75 +535,72 @@ class Entry(Py3kObject):
|
|||||||
def normalize_headers(self, headers):
|
def normalize_headers(self, headers):
|
||||||
new = {}
|
new = {}
|
||||||
for k in headers:
|
for k in headers:
|
||||||
new_k = '-'.join([s.title() for s in k.split('-')])
|
new_k = '-'.join([s.lower() for s in k.split('-')])
|
||||||
new[new_k] = headers[k]
|
new[new_k] = headers[k]
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def fill_filekind(self, fk, request):
|
def fill_filekind(self, fk, request):
|
||||||
req_info, method, req_body, req_headers = request
|
|
||||||
|
|
||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'Status': self.status,
|
'status': self.status,
|
||||||
'Date': now.strftime('%a, %d %b %Y %H:%M:%S GMT'),
|
'date': now.strftime('%a, %d %b %Y %H:%M:%S GMT'),
|
||||||
'Server': 'Python/HTTPretty',
|
'server': 'Python/HTTPretty',
|
||||||
'Connection': 'close',
|
'connection': 'close',
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.dynamic_response:
|
|
||||||
response = self.body(req_info, method, req_body, req_headers)
|
|
||||||
if isinstance(response, basestring):
|
|
||||||
body = response
|
|
||||||
new_headers = {}
|
|
||||||
else:
|
|
||||||
body, new_headers = response
|
|
||||||
else:
|
|
||||||
body = self.body
|
|
||||||
new_headers = {}
|
|
||||||
|
|
||||||
|
|
||||||
if self.forcing_headers:
|
if self.forcing_headers:
|
||||||
headers = self.forcing_headers
|
headers = self.forcing_headers
|
||||||
|
|
||||||
headers.update(new_headers)
|
if self.dynamic_response:
|
||||||
|
req_info, req_body, req_headers = request
|
||||||
|
response = self.body(req_info, self.method, req_body, req_headers)
|
||||||
|
if isinstance(response, basestring):
|
||||||
|
body = response
|
||||||
|
else:
|
||||||
|
body, new_headers = response
|
||||||
|
headers.update(new_headers)
|
||||||
|
else:
|
||||||
|
body = self.body
|
||||||
|
|
||||||
if self.adding_headers:
|
if self.adding_headers:
|
||||||
headers.update(self.adding_headers)
|
headers.update(self.normalize_headers(self.adding_headers))
|
||||||
|
|
||||||
headers = self.normalize_headers(headers)
|
headers = self.normalize_headers(headers)
|
||||||
|
|
||||||
status = headers.get('Status', self.status)
|
status = headers.get('status', self.status)
|
||||||
string_list = [
|
string_list = [
|
||||||
'HTTP/1.1 %d %s' % (status, STATUSES[status]),
|
'HTTP/1.1 %d %s' % (status, STATUSES[status]),
|
||||||
]
|
]
|
||||||
|
|
||||||
if 'Date' in headers:
|
if 'date' in headers:
|
||||||
string_list.append('Date: %s' % headers.pop('Date'))
|
string_list.append('date: %s' % headers.pop('date'))
|
||||||
|
|
||||||
if not self.forcing_headers:
|
if not self.forcing_headers:
|
||||||
content_type = headers.pop('Content-Type',
|
content_type = headers.pop('content-type',
|
||||||
'text/plain; charset=utf-8')
|
'text/plain; charset=utf-8')
|
||||||
|
|
||||||
body_length = self.body_length
|
body_length = self.body_length
|
||||||
if self.dynamic_response:
|
if self.dynamic_response:
|
||||||
body_length = len(body)
|
body_length = len(body)
|
||||||
content_length = headers.pop('Content-Length', body_length)
|
content_length = headers.pop('content-length', body_length)
|
||||||
|
|
||||||
string_list.append('Content-Type: %s' % content_type)
|
string_list.append('content-type: %s' % content_type)
|
||||||
if not self.streaming:
|
if not self.streaming:
|
||||||
string_list.append('Content-Length: %s' % content_length)
|
string_list.append('content-length: %s' % content_length)
|
||||||
|
|
||||||
string_list.append('Server: %s' % headers.pop('Server'))
|
string_list.append('server: %s' % headers.pop('server'))
|
||||||
|
|
||||||
for k, v in headers.items():
|
for k, v in headers.items():
|
||||||
string_list.append(
|
string_list.append(
|
||||||
'%s: %s' % (k, utf8(v)),
|
'{0}: {1}'.format(k, v),
|
||||||
)
|
)
|
||||||
|
|
||||||
fk.write("\n".join(string_list))
|
for item in string_list:
|
||||||
fk.write('\n\r\n')
|
fk.write(utf8(item) + b'\n')
|
||||||
|
|
||||||
|
fk.write(b'\r\n')
|
||||||
|
|
||||||
if self.streaming:
|
if self.streaming:
|
||||||
self.body, body = itertools.tee(body)
|
self.body, body = itertools.tee(body)
|
||||||
@ -608,25 +613,10 @@ class Entry(Py3kObject):
|
|||||||
|
|
||||||
|
|
||||||
def url_fix(s, charset='utf-8'):
|
def url_fix(s, charset='utf-8'):
|
||||||
import urllib
|
scheme, netloc, path, querystring, fragment = urlsplit(s)
|
||||||
import urlparse
|
path = quote(path, b'/%')
|
||||||
"""Sometimes you get an URL by a user that just isn't a real
|
querystring = quote_plus(querystring, b':&=')
|
||||||
URL because it contains unsafe characters like ' ' and so on. This
|
return urlunsplit((scheme, netloc, path, querystring, fragment))
|
||||||
function can fix some of the problems in a similar way browsers
|
|
||||||
handle data entered by the user:
|
|
||||||
|
|
||||||
>>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)')
|
|
||||||
'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
|
|
||||||
|
|
||||||
:param charset: The target charset for the URL if the url was
|
|
||||||
given as unicode string.
|
|
||||||
"""
|
|
||||||
if isinstance(s, unicode):
|
|
||||||
s = s.encode(charset, 'ignore')
|
|
||||||
scheme, netloc, path, qs, anchor = urlparse.urlsplit(s)
|
|
||||||
path = urllib.quote(path, '/%')
|
|
||||||
qs = urllib.quote_plus(qs, ':&=')
|
|
||||||
return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))
|
|
||||||
|
|
||||||
|
|
||||||
class URIInfo(Py3kObject):
|
class URIInfo(Py3kObject):
|
||||||
@ -639,7 +629,6 @@ class URIInfo(Py3kObject):
|
|||||||
query='',
|
query='',
|
||||||
fragment='',
|
fragment='',
|
||||||
scheme='',
|
scheme='',
|
||||||
method=None,
|
|
||||||
last_request=None):
|
last_request=None):
|
||||||
|
|
||||||
self.username = username or ''
|
self.username = username or ''
|
||||||
@ -653,11 +642,10 @@ class URIInfo(Py3kObject):
|
|||||||
port = 443
|
port = 443
|
||||||
|
|
||||||
self.port = port or 80
|
self.port = port or 80
|
||||||
self.path = url_fix(path) or ''
|
self.path = path or ''
|
||||||
self.query = query or ''
|
self.query = query or ''
|
||||||
self.scheme = scheme or (self.port is 80 and "http" or "https")
|
self.scheme = scheme or (self.port is 80 and "http" or "https")
|
||||||
self.fragment = fragment or ''
|
self.fragment = fragment or ''
|
||||||
self.method = method
|
|
||||||
self.last_request = last_request
|
self.last_request = last_request
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -675,17 +663,17 @@ class URIInfo(Py3kObject):
|
|||||||
return hash(text_type(self))
|
return hash(text_type(self))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
orig_hostname = self.hostname
|
self_tuple = (
|
||||||
orig_other = other.hostname
|
self.port,
|
||||||
|
decode_utf8(self.hostname),
|
||||||
self.hostname = None
|
url_fix(decode_utf8(self.path)),
|
||||||
other.hostname = None
|
)
|
||||||
result = text_type(self) == text_type(other)
|
other_tuple = (
|
||||||
|
other.port,
|
||||||
self.hostname = orig_hostname
|
decode_utf8(other.hostname),
|
||||||
other.hostname = orig_other
|
url_fix(decode_utf8(other.path)),
|
||||||
|
)
|
||||||
return result
|
return self_tuple == other_tuple
|
||||||
|
|
||||||
def full_url(self):
|
def full_url(self):
|
||||||
credentials = ""
|
credentials = ""
|
||||||
@ -693,21 +681,18 @@ class URIInfo(Py3kObject):
|
|||||||
credentials = "{0}:{1}@".format(
|
credentials = "{0}:{1}@".format(
|
||||||
self.username, self.password)
|
self.username, self.password)
|
||||||
|
|
||||||
# query = ""
|
result = "{scheme}://{credentials}{host}{path}".format(
|
||||||
# if self.query:
|
|
||||||
# query = "?{0}".format(self.query)
|
|
||||||
|
|
||||||
return "{scheme}://{credentials}{host}{path}".format(
|
|
||||||
scheme=self.scheme,
|
scheme=self.scheme,
|
||||||
credentials=credentials,
|
credentials=credentials,
|
||||||
host=self.hostname,
|
host=decode_utf8(self.hostname),
|
||||||
path=self.path,
|
path=decode_utf8(self.path)
|
||||||
#query=query
|
|
||||||
)
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_uri(cls, uri, entry):
|
def from_uri(cls, uri, entry):
|
||||||
result = urlsplit(uri)
|
result = urlsplit(uri)
|
||||||
|
POTENTIAL_HTTP_PORTS.append(int(result.port or 80))
|
||||||
return cls(result.username,
|
return cls(result.username,
|
||||||
result.password,
|
result.password,
|
||||||
result.hostname,
|
result.hostname,
|
||||||
@ -723,15 +708,16 @@ class URIMatcher(object):
|
|||||||
regex = None
|
regex = None
|
||||||
info = None
|
info = None
|
||||||
|
|
||||||
def __init__(self, uri, method, entries):
|
def __init__(self, uri, entries):
|
||||||
if type(uri).__name__ == 'SRE_Pattern':
|
if type(uri).__name__ == 'SRE_Pattern':
|
||||||
self.regex = uri
|
self.regex = uri
|
||||||
else:
|
else:
|
||||||
self.info = URIInfo.from_uri(uri, entries)
|
self.info = URIInfo.from_uri(uri, entries)
|
||||||
|
|
||||||
self.method = method
|
|
||||||
self.entries = entries
|
self.entries = entries
|
||||||
self.current_entry = 0
|
|
||||||
|
#hash of current_entry pointers, per method.
|
||||||
|
self.current_entries = {}
|
||||||
|
|
||||||
def matches(self, info):
|
def matches(self, info):
|
||||||
if self.info:
|
if self.info:
|
||||||
@ -740,22 +726,32 @@ class URIMatcher(object):
|
|||||||
return self.regex.search(info.full_url())
|
return self.regex.search(info.full_url())
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
wrap = 'URLMatcher({0} {1})'
|
wrap = 'URLMatcher({0})'
|
||||||
if self.info:
|
if self.info:
|
||||||
return wrap.format(text_type(self.info), self.method)
|
return wrap.format(text_type(self.info))
|
||||||
else:
|
else:
|
||||||
return wrap.format(self.regex.pattern, self.method)
|
return wrap.format(self.regex.pattern)
|
||||||
|
|
||||||
def get_next_entry(self):
|
def get_next_entry(self, method='GET'):
|
||||||
if self.current_entry >= len(self.entries):
|
"""Cycle through available responses, but only once.
|
||||||
self.current_entry = -1
|
Any subsequent requests will receive the last response"""
|
||||||
|
|
||||||
if not self.entries:
|
if method not in self.current_entries:
|
||||||
raise ValueError('I have no entries: %s' % self)
|
self.current_entries[method] = 0
|
||||||
|
|
||||||
entry = self.entries[self.current_entry]
|
#restrict selection to entries that match the requested method
|
||||||
if self.current_entry != -1:
|
entries_for_method = [e for e in self.entries if e.method == method]
|
||||||
self.current_entry += 1
|
|
||||||
|
if self.current_entries[method] >= len(entries_for_method):
|
||||||
|
self.current_entries[method] = -1
|
||||||
|
|
||||||
|
if not self.entries or not entries_for_method:
|
||||||
|
raise ValueError('I have no entries for method %s: %s'
|
||||||
|
% (method, self))
|
||||||
|
|
||||||
|
entry = entries_for_method[self.current_entries[method]]
|
||||||
|
if self.current_entries[method] != -1:
|
||||||
|
self.current_entries[method] += 1
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
@ -769,14 +765,15 @@ class HTTPretty(Py3kObject):
|
|||||||
u"""The URI registration class"""
|
u"""The URI registration class"""
|
||||||
_entries = {}
|
_entries = {}
|
||||||
latest_requests = []
|
latest_requests = []
|
||||||
GET = 'GET'
|
GET = b'GET'
|
||||||
PUT = 'PUT'
|
PUT = b'PUT'
|
||||||
POST = 'POST'
|
POST = b'POST'
|
||||||
DELETE = 'DELETE'
|
DELETE = b'DELETE'
|
||||||
HEAD = 'HEAD'
|
HEAD = b'HEAD'
|
||||||
PATCH = 'PATCH'
|
PATCH = b'PATCH'
|
||||||
METHODS = (GET, PUT, POST, DELETE, HEAD, PATCH)
|
METHODS = (GET, PUT, POST, DELETE, HEAD, PATCH)
|
||||||
last_request = HTTPrettyRequestEmpty()
|
last_request = HTTPrettyRequestEmpty()
|
||||||
|
_is_enabled = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def reset(cls):
|
def reset(cls):
|
||||||
@ -802,6 +799,9 @@ class HTTPretty(Py3kObject):
|
|||||||
responses=None, **headers):
|
responses=None, **headers):
|
||||||
|
|
||||||
if isinstance(responses, list) and len(responses) > 0:
|
if isinstance(responses, list) and len(responses) > 0:
|
||||||
|
for response in responses:
|
||||||
|
response.uri = uri
|
||||||
|
response.method = method
|
||||||
entries_for_this_uri = responses
|
entries_for_this_uri = responses
|
||||||
else:
|
else:
|
||||||
headers['body'] = body
|
headers['body'] = body
|
||||||
@ -813,11 +813,9 @@ class HTTPretty(Py3kObject):
|
|||||||
cls.Response(method=method, uri=uri, **headers),
|
cls.Response(method=method, uri=uri, **headers),
|
||||||
]
|
]
|
||||||
|
|
||||||
map(lambda e: setattr(e, 'uri', uri) or setattr(e, 'method', method),
|
matcher = URIMatcher(uri, entries_for_this_uri)
|
||||||
entries_for_this_uri)
|
|
||||||
|
|
||||||
matcher = URIMatcher(uri, method, entries_for_this_uri)
|
|
||||||
if matcher in cls._entries:
|
if matcher in cls._entries:
|
||||||
|
matcher.entries.extend(cls._entries[matcher])
|
||||||
del cls._entries[matcher]
|
del cls._entries[matcher]
|
||||||
|
|
||||||
cls._entries[matcher] = entries_for_this_uri
|
cls._entries[matcher] = entries_for_this_uri
|
||||||
@ -838,6 +836,7 @@ class HTTPretty(Py3kObject):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def disable(cls):
|
def disable(cls):
|
||||||
|
cls._is_enabled = False
|
||||||
socket.socket = old_socket
|
socket.socket = old_socket
|
||||||
socket.SocketType = old_socket
|
socket.SocketType = old_socket
|
||||||
socket._socketobject = old_socket
|
socket._socketobject = old_socket
|
||||||
@ -872,8 +871,13 @@ class HTTPretty(Py3kObject):
|
|||||||
ssl.sslwrap_simple = old_sslwrap_simple
|
ssl.sslwrap_simple = old_sslwrap_simple
|
||||||
ssl.__dict__['sslwrap_simple'] = old_sslwrap_simple
|
ssl.__dict__['sslwrap_simple'] = old_sslwrap_simple
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_enabled(cls):
|
||||||
|
return cls._is_enabled
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def enable(cls):
|
def enable(cls):
|
||||||
|
cls._is_enabled = True
|
||||||
socket.socket = fakesock.socket
|
socket.socket = fakesock.socket
|
||||||
socket._socketobject = fakesock.socket
|
socket._socketobject = fakesock.socket
|
||||||
socket.SocketType = fakesock.socket
|
socket.SocketType = fakesock.socket
|
||||||
@ -912,12 +916,29 @@ class HTTPretty(Py3kObject):
|
|||||||
|
|
||||||
def httprettified(test):
|
def httprettified(test):
|
||||||
"A decorator tests that use HTTPretty"
|
"A decorator tests that use HTTPretty"
|
||||||
@functools.wraps(test)
|
def decorate_class(klass):
|
||||||
def wrapper(*args, **kw):
|
for attr in dir(klass):
|
||||||
HTTPretty.reset()
|
if not attr.startswith('test_'):
|
||||||
HTTPretty.enable()
|
continue
|
||||||
try:
|
|
||||||
return test(*args, **kw)
|
attr_value = getattr(klass, attr)
|
||||||
finally:
|
if not hasattr(attr_value, "__call__"):
|
||||||
HTTPretty.disable()
|
continue
|
||||||
return wrapper
|
|
||||||
|
setattr(klass, attr, decorate_callable(attr_value))
|
||||||
|
return klass
|
||||||
|
|
||||||
|
def decorate_callable(test):
|
||||||
|
@functools.wraps(test)
|
||||||
|
def wrapper(*args, **kw):
|
||||||
|
HTTPretty.reset()
|
||||||
|
HTTPretty.enable()
|
||||||
|
try:
|
||||||
|
return test(*args, **kw)
|
||||||
|
finally:
|
||||||
|
HTTPretty.disable()
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
if isinstance(test, ClassTypes):
|
||||||
|
return decorate_class(test)
|
||||||
|
return decorate_callable(test)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user