From 0ab7a52f7c04998ae68ed7da442fce32293e0fb5 Mon Sep 17 00:00:00 2001 From: Robert Estelle Date: Wed, 31 Jul 2013 13:42:02 -0700 Subject: [PATCH] Merge settings when creating PreparedRequest, don't copy Request. --- requests/models.py | 59 ++++++++++++++++++++++++---------------------------- requests/sessions.py | 51 ++++++++++++++++++++++++--------------------- test_requests.py | 31 --------------------------- 3 files changed, 54 insertions(+), 87 deletions(-) diff --git a/requests/models.py b/requests/models.py index 7ebea01..f2d8e5f 100644 --- a/requests/models.py +++ b/requests/models.py @@ -28,8 +28,6 @@ from .compat import ( cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO, is_py2, chardet, json, builtin_str, basestring) -from copy import copy as shallowcopy - CONTENT_CHUNK_SIZE = 10 * 1024 ITER_CHUNK_SIZE = 512 @@ -216,39 +214,20 @@ class Request(RequestHooksMixin): def __repr__(self): return '' % (self.method) - def copy(self): - return Request( - method = self.method, - url = self.url, - auth = self.auth, - - # Copy mutable dict/list parameters so that altering one request - # does not alter the copy. - headers = shallowcopy(self.headers), - params = shallowcopy(self.params), - cookies = shallowcopy(self.cookies), - hooks = shallowcopy(self.hooks), - - files = shallowcopy(self.files), - data = shallowcopy(self.data), - ) - def prepare(self): """Constructs a :class:`PreparedRequest ` for transmission and returns it.""" p = PreparedRequest() - - p.prepare_method(self.method) - p.prepare_url(self.url, self.params) - p.prepare_headers(self.headers) - p.prepare_cookies(self.cookies) - p.prepare_body(self.data, self.files) - p.prepare_auth(self.auth, self.url) - # 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) - + p.prepare( + method=self.method, + url=self.url, + headers=self.headers, + files=self.files, + data=self.data, + params=self.params, + auth=self.auth, + cookies=self.cookies, + hooks=self.hooks, + ) return p @@ -283,6 +262,22 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): #: dictionary of callback hooks, for internal usage. self.hooks = default_hooks() + def prepare(self, method=None, url=None, headers=None, files=None, + data=None, params=None, auth=None, cookies=None, hooks=None): + """Prepares the the entire request with the given parameters.""" + + self.prepare_method(method) + self.prepare_url(url, params) + self.prepare_headers(headers) + self.prepare_cookies(cookies) + self.prepare_body(data, files) + self.prepare_auth(auth, url) + # 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 + self.prepare_hooks(hooks) + def __repr__(self): return '' % (self.method) diff --git a/requests/sessions.py b/requests/sessions.py index 2b6b607..b87bd86 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -228,11 +228,14 @@ class Session(SessionRedirectMixin): def __exit__(self, *args): self.close() - def update_request(self, request): - """Destructively updates/merges the settings of a :class:`Request` - object from those of the :class:`Session`. + def prepare_request(self, request): + """Constructs a :class:`PreparedRequest ` for + transmission and returns it. The :class:`PreparedRequest` has settings + merged from the :class:`Request ` instance and those of the + :class:`Session`. - :param request: mutable :class:`Request` instance. + :param request: :class:`Request` instance to prepare with this + session's settings. """ cookies = request.cookies or {} @@ -244,25 +247,26 @@ class Session(SessionRedirectMixin): merged_cookies = RequestsCookieJar() merged_cookies.update(self.cookies) merged_cookies.update(cookies) - request.cookies = merged_cookies - - # Gather clues from the surrounding environment. - if self.trust_env: - # Set environment's basic authentication if not explicitly set. - if not request.auth and not self.auth: - request.auth = get_netrc_auth(request.url) - # Merge settings from the request and the session. - request.params = merge_setting(request.params, self.params) - request.headers = merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict) - request.auth = merge_setting(request.auth, self.auth) - request.hooks = merge_setting(request.hooks, self.hooks) - request.method = request.method.upper() - def prepare_request(self, request): - req = request.copy() - self.update_request(req) - return req.prepare() + # Set environment's basic authentication if not explicitly set. + auth = request.auth + if self.trust_env and not auth and not self.auth: + auth = get_netrc_auth(request.url) + + p = PreparedRequest() + p.prepare( + method=request.method.upper(), + url=request.url, + files=request.files, + data=request.data, + headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), + params=merge_setting(request.params, self.params), + auth=merge_setting(auth, self.auth), + cookies=merged_cookies, + hooks=merge_setting(request.hooks, self.hooks), + ) + return p def request(self, method, url, params=None, @@ -319,8 +323,7 @@ class Session(SessionRedirectMixin): cookies = cookies, hooks = hooks, ) - self.update_request(req) - prep = req.prepare() + prep = self.prepare_request(req) proxies = proxies or {} @@ -438,7 +441,7 @@ class Session(SessionRedirectMixin): # It's possible that users might accidentally send a Request object. # Guard against that specific failure case. - if getattr(request, 'prepare', None): + if not isinstance(request, PreparedRequest): raise ValueError('You can only send PreparedRequests.') # Set up variables needed for resolve_redirects and dispatching of diff --git a/test_requests.py b/test_requests.py index 482881d..2fe8589 100755 --- a/test_requests.py +++ b/test_requests.py @@ -458,37 +458,6 @@ class RequestsTestCase(unittest.TestCase): self.assertTrue(hasattr(resp, 'hook_working')) - def test_request_copy(self): - a = requests.Request('POST', httpbin('post'), - headers={'oh': 'gee'}, - files={'some': 'thing'}, - data={'baz':'qux'}, - params={'foo': 'bar'}, - auth=object(), - cookies={'Some-Cookie':'Some Value'}, - hooks={'response': lambda r: r }) - b = a.copy() - - self.assertNotEqual(a, b) - self.assertEqual(a.method, b.method) - self.assertEqual(a.url, b.url) - self.assertEqual(a.headers, b.headers) - self.assertEqual(a.files, b.files) - self.assertEqual(a.data, b.data) - self.assertEqual(a.params, b.params) - self.assertEqual(a.auth, b.auth) - self.assertEqual(a.cookies, b.cookies) - self.assertEqual(a.hooks, b.hooks) - - a.headers['Foo'] = 'Bar' - self.assertNotEqual(a.headers, b.headers) - - a.data['x'] = 'y' - self.assertNotEqual(a.data, b.data) - - a.params['_'] = '?' - self.assertNotEqual(a.params, b.params) - def test_prepared_from_session(self): class DummyAuth(requests.auth.AuthBase): def __call__(self, r): -- 2.7.4