Ticket #17371: 17371.patch

File 17371.patch, 15.8 KB (added by Aymeric Augustin, 12 years ago)
  • django/test/client.py

    diff --git a/django/test/client.py b/django/test/client.py
    index c1764e2..3cc6a6e 100644
    a b def encode_file(boundary, key, file):  
    155155    ]
    156156
    157157
    158 
    159158class RequestFactory(object):
    160159    """
    161160    Class that lets you create mock Request objects for use in testing.
    class RequestFactory(object):  
    227226            return urllib.unquote(parsed[2])
    228227
    229228    def get(self, path, data={}, **extra):
    230         "Construct a GET request"
     229        "Construct a GET request."
    231230
    232231        parsed = urlparse(path)
    233232        r = {
    class RequestFactory(object):  
    270269        r.update(extra)
    271270        return self.request(**r)
    272271
    273     def options(self, path, data={}, **extra):
    274         "Constrict an OPTIONS request"
    275 
    276         parsed = urlparse(path)
    277         r = {
    278             'PATH_INFO':       self._get_path(parsed),
    279             'QUERY_STRING':    urlencode(data, doseq=True) or parsed[4],
    280             'REQUEST_METHOD': 'OPTIONS',
    281         }
    282         r.update(extra)
    283         return self.request(**r)
     272    def options(self, path, data='', content_type='application/octet-stream',
     273            **extra):
     274        "Construct an OPTIONS request."
     275        return self.generic('OPTIONS', path, data, content_type, **extra)
    284276
    285     def put(self, path, data={}, content_type=MULTIPART_CONTENT,
     277    def put(self, path, data='', content_type='application/octet-stream',
    286278            **extra):
    287279        "Construct a PUT request."
     280        return self.generic('PUT', path, data, content_type, **extra)
    288281
    289         put_data = self._encode_data(data, content_type)
     282    def delete(self, path, data='', content_type='application/octet-stream',
     283            **extra):
     284        "Construct a DELETE request."
     285        return self.generic('DELETE', path, data, content_type, **extra)
    290286
     287    def generic(self, method, path,
     288                data='', content_type='application/octet-stream', **extra):
    291289        parsed = urlparse(path)
     290        data = smart_str(data, settings.DEFAULT_CHARSET)
    292291        r = {
    293             'CONTENT_LENGTH': len(put_data),
    294             'CONTENT_TYPE':   content_type,
    295292            'PATH_INFO':      self._get_path(parsed),
    296293            'QUERY_STRING':   parsed[4],
    297             'REQUEST_METHOD': 'PUT',
    298             'wsgi.input':     FakePayload(put_data),
     294            'REQUEST_METHOD': method,
    299295        }
     296        if data:
     297            r.update({
     298                'CONTENT_LENGTH': len(data),
     299                'CONTENT_TYPE':   content_type,
     300                'wsgi.input':     FakePayload(data),
     301            })
    300302        r.update(extra)
    301303        return self.request(**r)
    302304
    303     def delete(self, path, data={}, **extra):
    304         "Construct a DELETE request."
    305 
    306         parsed = urlparse(path)
    307         r = {
    308             'PATH_INFO':       self._get_path(parsed),
    309             'QUERY_STRING':    urlencode(data, doseq=True) or parsed[4],
    310             'REQUEST_METHOD': 'DELETE',
    311         }
    312         r.update(extra)
    313         return self.request(**r)
    314 
    315 
    316305class Client(RequestFactory):
    317306    """
    318307    A class that can act as a client for testing purposes.
    class Client(RequestFactory):  
    445434            response = self._handle_redirects(response, **extra)
    446435        return response
    447436
    448     def options(self, path, data={}, follow=False, **extra):
     437    def options(self, path, data='', content_type='application/octet-stream',
     438            follow=False, **extra):
    449439        """
    450440        Request a response from the server using OPTIONS.
    451441        """
    452         response = super(Client, self).options(path, data=data, **extra)
     442        response = super(Client, self).options(path,
     443                data=data, content_type=content_type, **extra)
    453444        if follow:
    454445            response = self._handle_redirects(response, **extra)
    455446        return response
    456447
    457     def put(self, path, data={}, content_type=MULTIPART_CONTENT,
     448    def put(self, path, data='', content_type='application/octet-stream',
    458449            follow=False, **extra):
    459450        """
    460451        Send a resource to the server using PUT.
    461452        """
    462         response = super(Client, self).put(path, data=data, content_type=content_type, **extra)
     453        response = super(Client, self).put(path,
     454                data=data, content_type=content_type, **extra)
    463455        if follow:
    464456            response = self._handle_redirects(response, **extra)
    465457        return response
    466458
    467     def delete(self, path, data={}, follow=False, **extra):
     459    def delete(self, path, data='', content_type='application/octet-stream',
     460            follow=False, **extra):
    468461        """
    469462        Send a DELETE request to the server.
    470463        """
    471         response = super(Client, self).delete(path, data=data, **extra)
     464        response = super(Client, self).delete(path,
     465                data=data, content_type=content_type, **extra)
    472466        if follow:
    473467            response = self._handle_redirects(response, **extra)
    474468        return response
  • docs/releases/1.5.txt

    diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
    index 51e64bd..68cf885 100644
    a b Backwards incompatible changes in 1.5  
    7171    deprecation timeline for a given feature, its removal may appear as a
    7272    backwards incompatible change.
    7373
     74OPTIONS, PUT and DELETE requests in the test client
     75~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     76
     77Unlike GET and POST, these HTTP methods aren't implemented by web browsers.
     78Rather, they're used in APIs, which transfer data in various formats such as
     79JSON or XML. Since such requests may contain arbitrary data, Django doesn't
     80attempt to decode their body.
     81
     82However, the test client used to build a query string for OPTIONS and DELETE
     83requests like for GET, and a request body for PUT requests like for POST. This
     84encoding was arbitrary and inconsistent with Django's behavior when it
     85receives the requests, so it was removed in Django 1.5.
     86
     87If you were using the ``data`` parameter in an OPTIONS or a DELETE request,
     88you must convert it to a query string and append it to the ``path`` parameter.
     89
     90If you were using the ``data`` parameter in a PUT request without a
     91``content_type``, you must encode your data before passing it to the test
     92client and set the ``content_type`` argument.
     93
    7494Features deprecated in 1.5
    7595==========================
    7696
  • docs/topics/testing.txt

    diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt
    index d5ccc2d..98ee9ed 100644
    a b arguments at time of construction:  
    805805
    806806    .. method:: Client.head(path, data={}, follow=False, **extra)
    807807
    808         Makes a HEAD request on the provided ``path`` and returns a ``Response``
    809         object. Useful for testing RESTful interfaces. Acts just like
    810         :meth:`Client.get` except it does not return a message body.
     808        Makes a HEAD request on the provided ``path`` and returns a
     809        ``Response`` object. This method works just like :meth:`Client.get`,
     810        including the ``follow`` and ``extra`` arguments, except it does not
     811        return a message body.
    811812
    812         If you set ``follow`` to ``True`` the client will follow any redirects
    813         and a ``redirect_chain`` attribute will be set in the response object
    814         containing tuples of the intermediate urls and status codes.
    815 
    816     .. method:: Client.options(path, data={}, follow=False, **extra)
     813    .. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, **extra)
    817814
    818815        Makes an OPTIONS request on the provided ``path`` and returns a
    819816        ``Response`` object. Useful for testing RESTful interfaces.
    820817
    821         If you set ``follow`` to ``True`` the client will follow any redirects
    822         and a ``redirect_chain`` attribute will be set in the response object
    823         containing tuples of the intermediate urls and status codes.
     818        When ``data`` is provided, it is used as the request body, and
     819        a ``Content-Type`` header is set to ``content_type``.
    824820
    825         The ``extra`` argument acts the same as for :meth:`Client.get`.
     821        .. versionchanged:: 1.5
     822            ``Client.options`` used to process ``data`` like Client.get``.
    826823
    827     .. method:: Client.put(path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra)
     824        The ``follow`` and ``extra`` arguments act the same as for
     825        :meth:`Client.get`.
     826
     827    .. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, **extra)
    828828
    829829        Makes a PUT request on the provided ``path`` and returns a
    830         ``Response`` object. Useful for testing RESTful interfaces. Acts just
    831         like :meth:`Client.post` except with the PUT request method.
     830        ``Response`` object. ``data`` is Useful for testing RESTful interfaces.
    832831
    833         If you set ``follow`` to ``True`` the client will follow any redirects
    834         and a ``redirect_chain`` attribute will be set in the response object
    835         containing tuples of the intermediate urls and status codes.
     832        When ``data`` is provided, it is used as the request body, and
     833        a ``Content-Type`` header is set to ``content_type``.
     834
     835        .. versionchanged:: 1.5
     836            ``Client.put`` used to process ``data`` like Client.post``.
    836837
    837     .. method:: Client.delete(path, follow=False, **extra)
     838        The ``follow`` and ``extra`` arguments act the same as for
     839        :meth:`Client.get`.
     840
     841    .. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, **extra)
    838842
    839843        Makes an DELETE request on the provided ``path`` and returns a
    840844        ``Response`` object. Useful for testing RESTful interfaces.
    841845
    842         If you set ``follow`` to ``True`` the client will follow any redirects
    843         and a ``redirect_chain`` attribute will be set in the response object
    844         containing tuples of the intermediate urls and status codes.
     846        When ``data`` is provided, it is used as the request body, and
     847        a ``Content-Type`` header is set to ``content_type``.
     848
     849        .. versionchanged:: 1.5
     850            ``Client.delete`` used to process ``data`` like Client.get``.
     851
     852        The ``follow`` and ``extra`` arguments act the same as for
     853        :meth:`Client.get`.
    845854
    846         The ``extra`` argument acts the same as for :meth:`Client.get`.
    847855
    848856    .. method:: Client.login(**credentials)
    849857
  • tests/regressiontests/conditional_processing/models.py

    diff --git a/tests/regressiontests/conditional_processing/models.py b/tests/regressiontests/conditional_processing/models.py
    index f7f48bc..97aeff5 100644
    a b class ConditionalGet(TestCase):  
    6363
    6464    def testIfMatch(self):
    6565        self.client.defaults['HTTP_IF_MATCH'] = '"%s"' % ETAG
    66         response = self.client.put('/condition/etag/', {'data': ''})
     66        response = self.client.put('/condition/etag/')
    6767        self.assertEqual(response.status_code, 200)
    6868        self.client.defaults['HTTP_IF_MATCH'] = '"%s"' % EXPIRED_ETAG
    69         response = self.client.put('/condition/etag/', {'data': ''})
     69        response = self.client.put('/condition/etag/')
    7070        self.assertEqual(response.status_code, 412)
    7171
    7272    def testBothHeaders(self):
  • tests/regressiontests/test_client_regress/models.py

    diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
    index 4f057b9..e69a164 100644
    a b class AssertRedirectsTests(TestCase):  
    347347    def test_redirect_chain_options(self):
    348348        "A redirect chain will be followed from an initial OPTIONS request"
    349349        response = self.client.options('/test_client_regress/redirects/',
    350             {'nothing': 'to_send'}, follow=True)
     350            follow=True)
    351351        self.assertRedirects(response,
    352352            '/test_client_regress/no_template_view/', 301, 200)
    353353        self.assertEqual(len(response.redirect_chain), 3)
    class AssertRedirectsTests(TestCase):  
    355355    def test_redirect_chain_put(self):
    356356        "A redirect chain will be followed from an initial PUT request"
    357357        response = self.client.put('/test_client_regress/redirects/',
    358             {'nothing': 'to_send'}, follow=True)
     358            follow=True)
    359359        self.assertRedirects(response,
    360360            '/test_client_regress/no_template_view/', 301, 200)
    361361        self.assertEqual(len(response.redirect_chain), 3)
    class AssertRedirectsTests(TestCase):  
    363363    def test_redirect_chain_delete(self):
    364364        "A redirect chain will be followed from an initial DELETE request"
    365365        response = self.client.delete('/test_client_regress/redirects/',
    366             {'nothing': 'to_send'}, follow=True)
     366            follow=True)
    367367        self.assertRedirects(response,
    368368            '/test_client_regress/no_template_view/', 301, 200)
    369369        self.assertEqual(len(response.redirect_chain), 3)
    class RequestMethodStringDataTests(TestCase):  
    809809class QueryStringTests(TestCase):
    810810    def test_get_like_requests(self):
    811811        # See: https://code.djangoproject.com/ticket/10571.
    812         # Removed 'put' and 'delete' here as they are 'GET-like requests'
    813         for method_name in ('get','head','options'):
     812        for method_name in ('get', 'head'):
    814813            # A GET-like request can pass a query string as data
    815814            method = getattr(self.client, method_name)
    816815            response = method("/test_client_regress/request_data/", data={'foo':'whiz'})
    class UnicodePayloadTests(TestCase):  
    867866        response = self.client.post("/test_client_regress/parse_unicode_json/", json,
    868867                                    content_type="application/json")
    869868        self.assertEqual(response.content, json)
    870         response = self.client.put("/test_client_regress/parse_unicode_json/", json,
    871                                     content_type="application/json")
    872         self.assertEqual(response.content, json)
    873869
    874870    def test_unicode_payload_utf8(self):
    875871        "A non-ASCII unicode data encoded as UTF-8 can be POSTed"
    class UnicodePayloadTests(TestCase):  
    878874        response = self.client.post("/test_client_regress/parse_unicode_json/", json,
    879875                                    content_type="application/json; charset=utf-8")
    880876        self.assertEqual(response.content, json.encode('utf-8'))
    881         response = self.client.put("/test_client_regress/parse_unicode_json/", json,
    882                                     content_type="application/json; charset=utf-8")
    883         self.assertEqual(response.content, json.encode('utf-8'))
    884877
    885878    def test_unicode_payload_utf16(self):
    886879        "A non-ASCII unicode data encoded as UTF-16 can be POSTed"
    class UnicodePayloadTests(TestCase):  
    889882        response = self.client.post("/test_client_regress/parse_unicode_json/", json,
    890883                                    content_type="application/json; charset=utf-16")
    891884        self.assertEqual(response.content, json.encode('utf-16'))
    892         response = self.client.put("/test_client_regress/parse_unicode_json/", json,
    893                                     content_type="application/json; charset=utf-16")
    894         self.assertEqual(response.content, json.encode('utf-16'))
    895885
    896886    def test_unicode_payload_non_utf(self):
    897887        "A non-ASCII unicode data as a non-UTF based encoding can be POSTed"
    class UnicodePayloadTests(TestCase):  
    900890        response = self.client.post("/test_client_regress/parse_unicode_json/", json,
    901891                                    content_type="application/json; charset=koi8-r")
    902892        self.assertEqual(response.content, json.encode('koi8-r'))
    903         response = self.client.put("/test_client_regress/parse_unicode_json/", json,
    904                                     content_type="application/json; charset=koi8-r")
    905         self.assertEqual(response.content, json.encode('koi8-r'))
    906893
    907894class DummyFile(object):
    908895    def __init__(self, filename):
Back to Top