From 6671320cc5a158f9d377d565ee5f546296f96153 Mon Sep 17 00:00:00 2001 From: Kai Richard Koenig Date: Wed, 20 Mar 2013 11:15:36 +0100 Subject: [PATCH 1/2] Introduced more flexible callbacks. Callbacks are now called when the response is about to be written. A callback now takes the actual request as first parameter. And can be any object that is callable. Furthermore the signature of the return value has changed to a tuple returning the status code along with the headers and the body. This allows for more logic in the callbacks. Callbacks and regex url matching now work together because the real request is used for passing the url the callback instead of the Regex --- httpretty/__init__.py | 27 ++++++++++++---- tests/functional/test_httplib2.py | 4 +-- tests/functional/test_requests.py | 53 +++++++++++++++++++++++++++++-- tests/functional/test_urllib2.py | 4 +-- 4 files changed, 74 insertions(+), 14 deletions(-) diff --git a/httpretty/__init__.py b/httpretty/__init__.py index 4b24a67..a3298f3 100644 --- a/httpretty/__init__.py +++ b/httpretty/__init__.py @@ -320,7 +320,6 @@ class fakesock(object): for matcher, value in HTTPretty._entries.items(): if matcher.matches(info): entries = value - info = matcher.info break if not entries: @@ -328,6 +327,13 @@ class fakesock(object): return self._entry = matcher.get_next_entry(method) + # Attach more info to the entry + # So the callback can be more clever about what to do + # This does also fix the case where the callback + # would be handed a compiled regex as uri instead of the + # real uri + self._entry.info = info + self._entry.request = request def debug(*a, **kw): frame = inspect.stack()[0][0] @@ -464,17 +470,21 @@ class Entry(Py3kObject): self.method = method self.uri = uri + self.info = None + self.request = None - if isinstance(body, types.FunctionType): - self.body = body(method, uri, headers) - else: - self.body = body + self.body_is_callable = False + if hasattr(body,"__call__"): + self.body_is_callable = True + + self.body = body self.streaming = streaming - if not streaming: + if not streaming and not self.body_is_callable: self.body_length = len(self.body or '') else: self.body_length = 0 + self.adding_headers = adding_headers or {} self.forcing_headers = forcing_headers or {} self.status = int(status) @@ -541,8 +551,11 @@ class Entry(Py3kObject): headers.update(self.normalize_headers(self.adding_headers)) headers = self.normalize_headers(headers) - status = headers.get('status', self.status) + if self.body_is_callable: + status, headers, self.body = self.body(self.request,self.info.full_url(),headers) + headers.update({'content-length':len(self.body)}) + string_list = [ 'HTTP/1.1 %d %s' % (status, STATUSES[status]), ] diff --git a/tests/functional/test_httplib2.py b/tests/functional/test_httplib2.py index 2f927fa..271b254 100644 --- a/tests/functional/test_httplib2.py +++ b/tests/functional/test_httplib2.py @@ -266,8 +266,8 @@ def test_callback_response(now): (u"HTTPretty should all a callback function to be set as the body with" " httplib2") - def request_callback(method, uri, headers): - return "The {0} response from {1}".format(decode_utf8(method), uri) + def request_callback(request, uri, headers): + return [200,headers,"The {0} response from {1}".format(decode_utf8(request.method), uri)] HTTPretty.register_uri( HTTPretty.GET, "https://api.yahoo.com/test", diff --git a/tests/functional/test_requests.py b/tests/functional/test_requests.py index b682927..4d4f442 100644 --- a/tests/functional/test_requests.py +++ b/tests/functional/test_requests.py @@ -391,11 +391,11 @@ def test_multipart(): @httprettified @within(two=microseconds) def test_callback_response(now): - (u"HTTPretty should all a callback function to be set as the body with" + (u"HTTPretty should call a callback function and set its return value as the body of the response" " requests") - def request_callback(method, uri, headers): - return "The {0} response from {1}".format(decode_utf8(method), uri) + def request_callback(request, uri, headers): + return [200, headers,"The {0} response from {1}".format(decode_utf8(request.method), uri)] HTTPretty.register_uri( HTTPretty.GET, "https://api.yahoo.com/test", @@ -416,6 +416,53 @@ def test_callback_response(now): expect(response.text).to.equal("The POST response from https://api.yahoo.com/test_post") +@httprettified +@within(two=microseconds) +def test_callback_setting_headers_and_status_response(now): + (u"HTTPretty should call a callback function and uses it retur tuple as status code, headers and body" + " requests") + + def request_callback(request, uri, headers): + headers.update({'a':'b'}) + return [418,headers,"The {0} response from {1}".format(decode_utf8(request.method), uri)] + + HTTPretty.register_uri( + HTTPretty.GET, "https://api.yahoo.com/test", + body=request_callback) + + response = requests.get('https://api.yahoo.com/test') + expect(response.text).to.equal("The GET response from https://api.yahoo.com/test") + expect(response.headers).to.have.key('a').being.equal("b") + expect(response.status_code).to.be(418) + + HTTPretty.register_uri( + HTTPretty.POST, "https://api.yahoo.com/test_post", + body=request_callback) + + response = requests.post( + "https://api.yahoo.com/test_post", + {"username": "gabrielfalcao"} + ) + + expect(response.text).to.equal("The POST response from https://api.yahoo.com/test_post") + expect(response.headers).to.have.key('a').being.equal("b") + expect(response.status_code).to.be(418) + +@httprettified +def test_httpretty_should_allow_registering_regexes_and_give_a_proper_match_to_the_callback(): + u"HTTPretty should allow registering regexes with requests and giva a proper match to the callback" + + HTTPretty.register_uri( + HTTPretty.GET, + re.compile("https://api.yipit.com/v1/deal;brand=(?P\w+)"), + body=lambda method,uri,headers: [200,headers,uri] + ) + + response = requests.get('https://api.yipit.com/v1/deal;brand=gap?first_name=chuck&last_name=norris') + + expect(response.text).to.equal('https://api.yipit.com/v1/deal;brand=gap?first_name=chuck&last_name=norris') + expect(HTTPretty.last_request.method).to.equal('GET') + expect(HTTPretty.last_request.path).to.equal('/v1/deal;brand=gap?first_name=chuck&last_name=norris') @httprettified def test_httpretty_should_allow_registering_regexes(): diff --git a/tests/functional/test_urllib2.py b/tests/functional/test_urllib2.py index 9fc0f47..f76a6a5 100644 --- a/tests/functional/test_urllib2.py +++ b/tests/functional/test_urllib2.py @@ -284,8 +284,8 @@ def test_callback_response(now): (u"HTTPretty should all a callback function to be set as the body with" " urllib2") - def request_callback(method, uri, headers): - return "The {0} response from {1}".format(decode_utf8(method), uri) + def request_callback(request, uri, headers): + return [200, headers, "The {0} response from {1}".format(decode_utf8(request.method), uri)] HTTPretty.register_uri( HTTPretty.GET, "https://api.yahoo.com/test", From 23764312787bb369346ab76d95a3fd1ba08b18d5 Mon Sep 17 00:00:00 2001 From: Kai Richard Koenig Date: Wed, 20 Mar 2013 12:33:42 +0100 Subject: [PATCH 2/2] Fixed an issue were callbacks would override them selfs with str which breaks any subsequent requests --- httpretty/__init__.py | 4 +++- tests/functional/test_requests.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/httpretty/__init__.py b/httpretty/__init__.py index a3298f3..07c245a 100644 --- a/httpretty/__init__.py +++ b/httpretty/__init__.py @@ -475,6 +475,8 @@ class Entry(Py3kObject): self.body_is_callable = False if hasattr(body,"__call__"): + self.callable_body = body + self.body = None self.body_is_callable = True self.body = body @@ -553,7 +555,7 @@ class Entry(Py3kObject): headers = self.normalize_headers(headers) status = headers.get('status', self.status) if self.body_is_callable: - status, headers, self.body = self.body(self.request,self.info.full_url(),headers) + status, headers, self.body = self.callable_body(self.request,self.info.full_url(),headers) headers.update({'content-length':len(self.body)}) string_list = [ diff --git a/tests/functional/test_requests.py b/tests/functional/test_requests.py index 4d4f442..bdf43d6 100644 --- a/tests/functional/test_requests.py +++ b/tests/functional/test_requests.py @@ -416,6 +416,25 @@ def test_callback_response(now): expect(response.text).to.equal("The POST response from https://api.yahoo.com/test_post") +@httprettified +@within(two=microseconds) +def test_callback_body_remains_callable_for_any_subsequent_requests(now): + (u"HTTPretty should call a callback function more than one" + " requests") + + def request_callback(request, uri, headers): + return [200, headers,"The {0} response from {1}".format(decode_utf8(request.method), uri)] + + HTTPretty.register_uri( + HTTPretty.GET, "https://api.yahoo.com/test", + body=request_callback) + + response = requests.get('https://api.yahoo.com/test') + expect(response.text).to.equal("The GET response from https://api.yahoo.com/test") + + response = requests.get('https://api.yahoo.com/test') + expect(response.text).to.equal("The GET response from https://api.yahoo.com/test") + @httprettified @within(two=microseconds) def test_callback_setting_headers_and_status_response(now):