Merge settings when creating PreparedRequest, don't copy Request.
authorRobert Estelle <robert@btbapp.com>
Wed, 31 Jul 2013 20:42:02 +0000 (13:42 -0700)
committerRobert Estelle <robert@btbapp.com>
Wed, 31 Jul 2013 20:42:02 +0000 (13:42 -0700)
requests/models.py
requests/sessions.py
test_requests.py

index 7ebea01..f2d8e5f 100644 (file)
@@ -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 '<Request [%s]>' % (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 <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 '<PreparedRequest [%s]>' % (self.method)
 
index 2b6b607..b87bd86 100644 (file)
@@ -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 <PreparedRequest>` for
+        transmission and returns it. The :class:`PreparedRequest` has settings
+        merged from the :class:`Request <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
index 482881d..2fe8589 100755 (executable)
@@ -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):