Skip to content

Commit 254444d

Browse files
committed
Added httpretty.register decorator
1 parent df02999 commit 254444d

File tree

3 files changed

+155
-72
lines changed

3 files changed

+155
-72
lines changed

httpretty/core.py

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# #!/usr/bin/env python
1+
# !/usr/bin/env python
22
# -*- coding: utf-8 -*-
33
# <HTTPretty - HTTP client mock for Python>
44
# Copyright (C) <2011-2013> Gabriel Falcão <[email protected]>
@@ -107,6 +107,7 @@
107107

108108

109109
class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
110+
110111
"""Represents a HTTP request. It takes a valid multi-line, `\r\n`
111112
separated string with HTTP headers and parse them out using the
112113
internal `parse_request` method.
@@ -138,6 +139,7 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
138139
`content-type` headers values: 'application/json' or
139140
'application/x-www-form-urlencoded'
140141
"""
142+
141143
def __init__(self, headers, body=''):
142144
# first of all, lets make sure that if headers or body are
143145
# unicode strings, it must be converted into a utf-8 encoded
@@ -229,12 +231,14 @@ class HTTPrettyRequestEmpty(object):
229231

230232

231233
class FakeSockFile(StringIO):
234+
232235
def close(self):
233236
self.socket.close()
234237
StringIO.close(self)
235238

236239

237240
class FakeSSLSocket(object):
241+
238242
def __init__(self, sock, *args, **kw):
239243
self._httpretty_sock = sock
240244

@@ -243,6 +247,7 @@ def __getattr__(self, attr):
243247

244248

245249
class fakesock(object):
250+
246251
class socket(object):
247252
_entry = None
248253
debuglevel = 0
@@ -380,7 +385,8 @@ def sendall(self, data, *args, **kw):
380385
is_parsing_headers = False
381386

382387
if not self._entry:
383-
# If the previous request wasn't mocked, don't mock the subsequent sending of data
388+
# If the previous request wasn't mocked, don't mock the subsequent sending
389+
# of data
384390
return self.real_sendall(data, *args, **kw)
385391

386392
self.fd.seek(0)
@@ -492,6 +498,7 @@ def fake_getaddrinfo(
492498

493499

494500
class Entry(BaseClass):
501+
495502
def __init__(self, method, uri, body,
496503
adding_headers=None,
497504
forcing_headers=None,
@@ -543,15 +550,15 @@ def validate(self):
543550
igot = int(got)
544551
except ValueError:
545552
warnings.warn(
546-
'HTTPretty got to register the Content-Length header ' \
553+
'HTTPretty got to register the Content-Length header '
547554
'with "%r" which is not a number' % got,
548555
)
549556

550557
if igot > self.body_length:
551558
raise HTTPrettyError(
552-
'HTTPretty got inconsistent parameters. The header ' \
553-
'Content-Length you registered expects size "%d" but ' \
554-
'the body you registered for that has actually length ' \
559+
'HTTPretty got inconsistent parameters. The header '
560+
'Content-Length you registered expects size "%d" but '
561+
'the body you registered for that has actually length '
555562
'"%d".' % (
556563
igot, self.body_length,
557564
)
@@ -588,7 +595,8 @@ def fill_filekind(self, fk):
588595
headers = self.normalize_headers(headers)
589596
status = headers.get('status', self.status)
590597
if self.body_is_callable:
591-
status, headers, self.body = self.callable_body(self.request, self.info.full_url(), headers)
598+
status, headers, self.body = self.callable_body(
599+
self.request, self.info.full_url(), headers)
592600
headers.update({
593601
'content-length': len(self.body)
594602
})
@@ -640,6 +648,7 @@ def url_fix(s, charset='utf-8'):
640648

641649

642650
class URIInfo(BaseClass):
651+
643652
def __init__(self,
644653
username='',
645654
password='',
@@ -763,7 +772,7 @@ def __init__(self, uri, entries, match_querystring=False):
763772

764773
self.entries = entries
765774

766-
#hash of current_entry pointers, per method.
775+
# hash of current_entry pointers, per method.
767776
self.current_entries = {}
768777

769778
def matches(self, info):
@@ -787,7 +796,7 @@ def get_next_entry(self, method, info, request):
787796
if method not in self.current_entries:
788797
self.current_entries[method] = 0
789798

790-
#restrict selection to entries that match the requested method
799+
# restrict selection to entries that match the requested method
791800
entries_for_method = [e for e in self.entries if e.method == method]
792801

793802
if self.current_entries[method] >= len(entries_for_method):
@@ -818,6 +827,7 @@ def __eq__(self, other):
818827

819828

820829
class httpretty(HttpBaseClass):
830+
821831
"""The URI registration class"""
822832
_entries = {}
823833
latest_requests = []
@@ -840,13 +850,14 @@ def record(cls, filename, indentation=4, encoding='utf-8'):
840850
try:
841851
import urllib3
842852
except ImportError:
843-
raise RuntimeError('HTTPretty requires urllib3 installed for recording actual requests.')
844-
853+
raise RuntimeError(
854+
'HTTPretty requires urllib3 installed for recording actual requests.')
845855

846856
http = urllib3.PoolManager()
847857

848858
cls.enable()
849859
calls = []
860+
850861
def record_request(request, uri, headers):
851862
cls.disable()
852863

@@ -885,7 +896,8 @@ def playback(cls, origin):
885896
for item in data:
886897
uri = item['request']['uri']
887898
method = item['request']['method']
888-
cls.register_uri(method, uri, body=item['response']['body'], forcing_headers=item['response']['headers'])
899+
cls.register_uri(
900+
method, uri, body=item['response']['body'], forcing_headers=item['response']['headers'])
889901

890902
yield
891903
cls.disable()
@@ -1034,19 +1046,27 @@ def enable(cls):
10341046
ssl.__dict__['sslwrap_simple'] = fake_wrap_socket
10351047

10361048

1037-
def httprettified(test):
1038-
"A decorator tests that use HTTPretty"
1039-
def decorate_class(klass):
1040-
for attr in dir(klass):
1041-
if not attr.startswith('test_'):
1042-
continue
1049+
def decorate_class(klass, callable_fn):
1050+
"""
1051+
A helper method to apply callable_fn to class attributes.
1052+
It's not intended for direct use.
1053+
"""
1054+
for attr in dir(klass):
1055+
if not attr.startswith('test_'):
1056+
continue
1057+
1058+
attr_value = getattr(klass, attr)
1059+
if not hasattr(attr_value, "__call__"):
1060+
continue
1061+
1062+
setattr(klass, attr, callable_fn(attr_value))
1063+
return klass
10431064

1044-
attr_value = getattr(klass, attr)
1045-
if not hasattr(attr_value, "__call__"):
1046-
continue
10471065

1048-
setattr(klass, attr, decorate_callable(attr_value))
1049-
return klass
1066+
def httprettified(test):
1067+
"""
1068+
A decorator that activates HTTPretty.
1069+
"""
10501070

10511071
def decorate_callable(test):
10521072
@functools.wraps(test)
@@ -1060,5 +1080,30 @@ def wrapper(*args, **kw):
10601080
return wrapper
10611081

10621082
if isinstance(test, ClassTypes):
1063-
return decorate_class(test)
1083+
return decorate_class(test, decorate_callable)
10641084
return decorate_callable(test)
1085+
1086+
1087+
def register(**dec_kwargs):
1088+
"""
1089+
A decorator that activates HTTPretty and registers an uri path,
1090+
by given keyword arguments.
1091+
"""
1092+
1093+
def decorator(func):
1094+
def decorate_callable(func):
1095+
@functools.wraps(func)
1096+
def wrapper(*args, **kw):
1097+
httpretty.reset()
1098+
httpretty.enable()
1099+
httpretty.register_uri(**dec_kwargs)
1100+
try:
1101+
return func(*args, **kw)
1102+
finally:
1103+
httpretty.disable()
1104+
return wrapper
1105+
1106+
if isinstance(func, ClassTypes):
1107+
return decorate_class(func, decorate_callable)
1108+
return decorate_callable(func)
1109+
return decorator

tests/functional/test_decorator.py

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# coding: utf-8
2+
from unittest import TestCase
3+
from sure import expect
4+
from httpretty import httprettified, HTTPretty
5+
from httpretty.core import register
6+
7+
try:
8+
import urllib.request as urllib2
9+
except ImportError:
10+
import urllib2
11+
12+
13+
@httprettified
14+
def test_httprettified_decorator():
15+
HTTPretty.register_uri(
16+
HTTPretty.GET, 'http://localhost/',
17+
body='glub glub')
18+
19+
fd = urllib2.urlopen('http://localhost/')
20+
contents = fd.read()
21+
fd.close()
22+
expect(contents).to.equal(b'glub glub')
23+
24+
25+
@httprettified
26+
class HTTPrettifiedClass(TestCase):
27+
28+
def setUp(self):
29+
self.assertFalse(HTTPretty.is_enabled())
30+
31+
def tearDown(self):
32+
self.assertFalse(HTTPretty.is_enabled())
33+
34+
def test_decorated(self):
35+
HTTPretty.register_uri(
36+
HTTPretty.GET, 'http://localhost/',
37+
body='glub glub')
38+
39+
fd = urllib2.urlopen('http://localhost/')
40+
contents = fd.read()
41+
fd.close()
42+
43+
expect(contents).to.equal(b'glub glub')
44+
45+
def test_decorated2(self):
46+
HTTPretty.register_uri(
47+
HTTPretty.GET, 'http://localhost/',
48+
body='buble buble')
49+
50+
fd = urllib2.urlopen('http://localhost/')
51+
contents = fd.read()
52+
fd.close()
53+
54+
expect(contents).to.equal(b'buble buble')
55+
56+
57+
@register(method=HTTPretty.GET, uri='http://localhost/', body='glub glub')
58+
def test_register_uri_decorator():
59+
fd = urllib2.urlopen('http://localhost/')
60+
contents = fd.read()
61+
fd.close()
62+
expect(contents).to.equal(b'glub glub')
63+
64+
65+
@register(method=HTTPretty.GET, uri='http://localhost/', body='bubble pop')
66+
class HTTPregisterClass(TestCase):
67+
68+
def setUp(self):
69+
self.assertFalse(HTTPretty.is_enabled())
70+
71+
def tearDown(self):
72+
self.assertFalse(HTTPretty.is_enabled())
73+
74+
def test_decorated(self):
75+
fd = urllib2.urlopen('http://localhost/')
76+
contents = fd.read()
77+
fd.close()
78+
79+
expect(contents).to.equal(b'bubble pop')
80+
81+
def test_decorated2(self):
82+
fd = urllib2.urlopen('http://localhost/')
83+
contents = fd.read()
84+
fd.close()
85+
86+
expect(contents).to.equal(b'bubble pop')

0 commit comments

Comments
 (0)