Ticket #16494: httpresponse_content.diff

File httpresponse_content.diff, 6.2 KB (added by PaulM, 4 years ago)

Converts non-string inputs to strings just before output. Adds tests.

  • django/http/__init__.py

    diff --git a/django/http/__init__.py b/django/http/__init__.py
    index c97b402..a732a53 100644
    a b class HttpResponse(object): 
    543543        if not content_type:
    544544            content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE,
    545545                    self._charset)
    546         if not isinstance(content, basestring) and hasattr(content, '__iter__'):
    547             self._container = content
    548             self._is_string = False
    549         else:
    550             self._container = [content]
    551             self._is_string = True
     546        HttpResponse._set_content(self, content)
    552547        self.cookies = SimpleCookie()
    553548        if status:
    554549            self.status_code = status
    class HttpResponse(object): 
    648643
    649644    def _get_content(self):
    650645        if self.has_header('Content-Encoding'):
    651             return ''.join(self._container)
    652         return smart_str(''.join(self._container), self._charset)
     646            return ''.join([str(e) for e in self._container])
     647        return ''.join([smart_str(e, self._charset) for e in self._container])
    653648
    654649    def _set_content(self, value):
    655         self._container = [value]
    656         self._is_string = True
     650        if hasattr(value, '__iter__'):
     651            self._container = value
     652            self._base_content_is_iter = True
     653        else:
     654            self._container = [value]
     655            self._base_content_is_iter = False
    657656
    658657    content = property(_get_content, _set_content)
    659658
    class HttpResponse(object): 
    674673    # The remaining methods partially implement the file-like object interface.
    675674    # See http://docs.python.org/lib/bltin-file-objects.html
    676675    def write(self, content):
    677         if not self._is_string:
     676        if self._base_content_is_iter:
    678677            raise Exception("This %s instance is not writable" % self.__class__)
    679678        self._container.append(content)
    680679
    class HttpResponse(object): 
    682681        pass
    683682
    684683    def tell(self):
    685         if not self._is_string:
     684        if self._base_content_is_iter:
    686685            raise Exception("This %s instance cannot tell its position" % self.__class__)
    687         return sum([len(chunk) for chunk in self._container])
     686        return sum([len(str(chunk)) for chunk in self._container])
    688687
    689688class HttpResponseRedirect(HttpResponse):
    690689    status_code = 302
  • docs/ref/request-response.txt

    diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
    index 295fba1..1bbee00 100644
    a b Attributes 
    588588
    589589.. attribute:: HttpResponse.content
    590590
    591     A normal Python string representing the content, encoded from a Unicode
     591    A string representing the content, encoded from a Unicode
    592592    object if necessary.
    593593
    594594.. attribute:: HttpResponse.status_code
    Methods 
    604604    string) and MIME type. The :setting:`DEFAULT_CONTENT_TYPE` is
    605605    ``'text/html'``.
    606606
    607     ``content`` can be an iterator or a string. If it's an iterator, it should
    608     return strings, and those strings will be joined together to form the
    609     content of the response.
     607    ``content`` should be an iterator or a string. If it's an
     608    iterator, it should return strings, and those strings will be
     609    joined together to form the content of the response. If it is not
     610    an iterator or a string, it will be converted to a string when
     611    accessed.
    610612
    611613    ``status`` is the `HTTP Status code`_ for the response.
    612614
  • tests/regressiontests/httpwrappers/tests.py

    diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py
    index 66bd804..8106556 100644
    a b class HttpResponseTests(unittest.TestCase): 
    216216        r['value'] = u'test value'
    217217        self.assertTrue(isinstance(r['value'], str))
    218218
    219         # An error is raised ~hen a unicode object with non-ascii is assigned.
     219        # An error is raised when a unicode object with non-ascii is assigned.
    220220        self.assertRaises(UnicodeEncodeError, r.__setitem__, 'value', u't\xebst value')
    221221
    222222        # An error is raised when  a unicode object with non-ASCII format is
    223223        # passed as initial mimetype or content_type.
    224224        self.assertRaises(UnicodeEncodeError, HttpResponse,
    225                 mimetype=u't\xebst value')
     225                content_type=u't\xebst value')
    226226
    227227        # HttpResponse headers must be convertible to ASCII.
    228228        self.assertRaises(UnicodeEncodeError, HttpResponse,
    class HttpResponseTests(unittest.TestCase): 
    250250        r = HttpResponse()
    251251        self.assertEqual(r.get('test'), None)
    252252
     253    def test_non_string_content(self):
     254        #Bug 16494: HttpResponse should behave consistently with non-strings
     255        r = HttpResponse(12345)
     256        self.assertEqual(r.content, '12345')
     257
     258        #test content via property
     259        r = HttpResponse()
     260        r.content = 12345
     261        self.assertEqual(r.content, '12345')
     262
     263    def test_iter_content(self):
     264        r = HttpResponse(['abc', 'def', 'ghi'])
     265        self.assertEqual(r.content, 'abcdefghi')
     266
     267        #test iter content via property
     268        r = HttpResponse()
     269        r.content = ['idan', 'alex', 'jacob']
     270        self.assertEqual(r.content, 'idanalexjacob')
     271
     272        r = HttpResponse()
     273        r.content = [1, 2, 3]
     274        self.assertEqual(r.content, '123')
     275
     276        #test retrieval explicitly using iter and odd inputs
     277        r = HttpResponse()
     278        r.content = ['1', u'2', 3, unichr(1950)]
     279        result = []
     280        my_iter = r.__iter__()
     281        while True:
     282            try:
     283                result.append(my_iter.next())
     284            except StopIteration:
     285                break
     286        #'\xde\x9e' == unichr(1950).encode('utf-8')
     287        self.assertEqual(result, ['1', '2', '3', '\xde\x9e'])
     288        self.assertEqual(r.content, '123\xde\x9e')
     289
     290        #with Content-Encoding header
     291        r = HttpResponse([1,1,2,4,8])
     292        r['Content-Encoding'] = 'winning'
     293        self.assertEqual(r.content, '11248')
     294        r.content = [unichr(1950),]
     295        self.assertRaises(UnicodeEncodeError,
     296                          getattr, r, 'content')
     297
    253298class CookieTests(unittest.TestCase):
    254299    def test_encode(self):
    255300        """
Back to Top