Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Make hooks work with prepared requests #1099

merged 7 commits into from

3 participants


:+1: This is a prerequisite for #1117 (or co-requisite)

@sigmavirus24 sigmavirus24 referenced this pull request

Fix #1106 #1117


@kennethreitz this is definitely a pull request you should merge. It complements #1117 which you already merged.


@sprt can you update this? It's a bit out of date now :)


Unfortunately, this isn't going to work because we made incompatible changes as I relied on the fact that the adapter would call dispatch_hook().

I'm not sure how to handle this; when using sessions Session.send() should call dispatch_hook() and when using connection adapters directly, BaseAdapter.send() should.


@sprt, I'll work on this.


@kennethreitz, @sprt makes a valid point. My concern is dispatching a hook more than once. Someone using requests in @sprt's case has a valid reason to believe the hook should be called which makes it the responsibility of either the adapter's send or the session's send method. The former also catches the case where a user just instantiates the adapter and uses that directly (which is a bad idea). The latter would allow the hook to be dispatched once, and would remove it from the Session.request and <Adapter>.send. This I see as being the preferable option, but I wanted to make sure this is okay.

The problem, of course, is that only request has the hooks passed by the user. Naturally, this shouldn't be a problem because the prepared request has those hooks and the auth hooks generated by the authentication handler, so we can just rely on that. No where in between are hooks removed. A rogue adapter may do this in its send method, but that would be author's problem, not ours.


The changes are in sigmavirus24/requests@pr/1099


@sprt those changes pass with your tests. If you like them merge them into your branch, they'll be added to this pull and @kennethreitz could accept it.


Done, sorry for the FF

What about adapters though?


Sorry, I meant merge it from the branch I specified above (git pull git:// pr/1099).

My patch places the dispatch after the request is returned from the adapter in Session.send. This satisfies your test and regular hooks as well.

@kennethreitz kennethreitz merged commit 22623bd into kennethreitz:master

1 check passed

Details default The Travis build passed



Ah, I missed the merge. Disregard my comment above.


@sigmavirus24 was this not ready? send another pr :)


So what's the consensus on instantiating the adapter and using that directly? Because this is still broken. Should hooks be dispatched in the adapter's send() method too or should users be told to use sessions?


Ah indeed, forgot about mounting!

Then I suggest @kennethreitz edit his blog post (cf. "Connection Adapters") :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 12, 2013
  1. @sprt
Commits on Jan 23, 2013
  1. @sprt

    Merge remote-tracking branch 'upstream/master'

    sprt authored
  2. @sprt

    Add missing import

    sprt authored
  3. @sprt

    Refactor prepare_hooks()

    sprt authored
  4. @sigmavirus24
  5. @sigmavirus24

    Only call the hook once.

    sigmavirus24 authored
Commits on Jan 24, 2013
  1. @sprt

    Merge branch 'master' of git://

    sprt authored
This page is out of date. Refresh to see the latest.
Showing with 27 additions and 10 deletions.
  1. +7 −0 requests/
  2. +7 −10 requests/
  3. +13 −0
7 requests/
@@ -225,6 +225,8 @@ def prepare(self):
# Note that prepare_auth must be last to enable authentication schemes
# such as OAuth to work on a fully prepared request.
+ # This MUST go after prepare_auth. Authenticators could add a hook
+ p.prepare_hooks(self.hooks)
return p
@@ -421,6 +423,11 @@ def prepare_cookies(self, cookies):
if cookie_header is not None:
self.headers['Cookie'] = cookie_header
+ def prepare_hooks(self, hooks):
+ """Prepares the given hooks."""
+ for event in hooks:
+ self.register_hook(event, hooks[event])
class Response(object):
"""The :class:`Response <Response>` object, which contains a
17 requests/
@@ -13,7 +13,7 @@
from .compat import cookielib
from .cookies import cookiejar_from_dict
from .models import Request
-from .hooks import dispatch_hook, default_hooks
+from .hooks import default_hooks, dispatch_hook
from .utils import from_key_val_list, default_headers
from .exceptions import TooManyRedirects, InvalidSchema
@@ -130,8 +130,9 @@ def resolve_redirects(self, resp, req, stream=False, timeout=None, verify=True,
- proxies=proxies
- )
+ proxies=proxies,
+ hooks=req.hooks,
+ )
i += 1
yield resp
@@ -275,10 +276,6 @@ def request(self, method, url,
# Prepare the Request.
prep = req.prepare()
- # If auth hooks are present, they aren't passed to `dispatch_hook`
- # As such, we need to update the original hooks dictionary with them
- hooks.update(prep.hooks)
# Send the request.
resp = self.send(prep, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies)
@@ -298,9 +295,6 @@ def request(self, method, url,
resp = history.pop()
resp.history = tuple(history)
- # Response manipulation hook.
- resp = dispatch_hook('response', hooks, resp)
return resp
def get(self, url, **kwargs):
@@ -374,8 +368,11 @@ def delete(self, url, **kwargs):
def send(self, request, **kwargs):
"""Send a given PreparedRequest."""
+ hooks = request.hooks
adapter = self.get_adapter(url=request.url)
r = adapter.send(request, **kwargs)
+ # Response manipulation hooks
+ r = dispatch_hook('response', hooks, r)
return r
def get_adapter(self, url):
@@ -261,6 +261,19 @@ def test_custom_content_type(self):
self.assertEqual(r.status_code, 200)
self.assertTrue(b"text/py-content-type" in r.request.body)
+ def test_prepared_request_hook(self):
+ def hook(resp):
+ resp.hook_working = True
+ return resp
+ req = requests.Request('GET', HTTPBIN, hooks={'response': hook})
+ prep = req.prepare()
+ s = requests.Session()
+ resp = s.send(prep)
+ self.assertTrue(hasattr(resp, 'hook_working'))
def test_links(self):
url = ''
r = requests.head(url=url)
Something went wrong with that request. Please try again.