Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #2744 by reverting #2706 on 1.7-branch. #2747

Merged
merged 2 commits into from
Aug 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
unreleased
==========

Bug Fixes
---------

- Revert changes from #2706 released in Pyramid 1.7.1. JSON renderers will
continue to return unicode data instead of UTF-8 encoded bytes. This means
that WebOb responses are still expected to handle unicode data even though
JSON does not have a charset.
See https://github.com/Pylons/pyramid/issues/2744

.. _changes_1.7.1:

1.7.1 (2016-08-16)
Expand Down
17 changes: 3 additions & 14 deletions pyramid/renderers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import json
import os
import re
import warnings

from zope.interface import (
implementer,
Expand Down Expand Up @@ -273,7 +272,7 @@ def _render(value, system):
if ct == response.default_content_type:
response.content_type = 'application/json'
default = self._make_default(request)
return self.serializer(value, default=default, **self.kw).encode('UTF-8')
return self.serializer(value, default=default, **self.kw)

return _render

Expand Down Expand Up @@ -380,7 +379,7 @@ def _render(value, system):
raise HTTPBadRequest('Invalid JSONP callback function name.')

ct = 'application/javascript'
body = '/**/{0}({1});'.format(callback, val).encode('UTF-8')
body = '/**/{0}({1});'.format(callback, val)
response = request.response
if response.content_type == response.default_content_type:
response.content_type = ct
Expand Down Expand Up @@ -468,17 +467,7 @@ def _make_response(self, result, request):

if result is not None:
if isinstance(result, text_type):
if response.charset is None:
warnings.warn(
"Renderer returned a result of type {0}, "
"however the response Content-Type <{1}> does not "
"have a charset. Implicitly encoding the result as "
"UTF-8.".format(type(result), response.content_type),
RuntimeWarning
)
response.body = result.encode('UTF-8')
else:
response.text = result
response.text = result
elif isinstance(result, bytes):
response.body = result
elif hasattr(result, '__iter__'):
Expand Down
4 changes: 2 additions & 2 deletions pyramid/tests/test_config/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2168,7 +2168,7 @@ def test_add_notfound_view_with_renderer(self):
ctx_iface=implementedBy(HTTPNotFound),
request_iface=IRequest)
result = view(None, request)
self._assertBody(result, b'{}')
self._assertBody(result, '{}')

def test_add_forbidden_view_with_renderer(self):
from zope.interface import implementedBy
Expand All @@ -2185,7 +2185,7 @@ def test_add_forbidden_view_with_renderer(self):
ctx_iface=implementedBy(HTTPForbidden),
request_iface=IRequest)
result = view(None, request)
self._assertBody(result, b'{}')
self._assertBody(result, '{}')

def test_set_view_mapper(self):
from pyramid.interfaces import IViewMapperFactory
Expand Down
31 changes: 9 additions & 22 deletions pyramid/tests/test_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def _makeOne(self, **kw):
def test_it(self):
renderer = self._makeOne()(None)
result = renderer({'a':1}, {})
self.assertEqual(result, b'{"a": 1}')
self.assertEqual(result, '{"a": 1}')

def test_with_request_content_type_notset(self):
request = testing.DummyRequest()
Expand All @@ -43,7 +43,7 @@ def adapter(obj, req):
renderer = self._makeOne()
renderer.add_adapter(datetime, adapter)
result = renderer(None)({'a':now}, {'request':request})
self.assertEqual(result, '{{"a": "{0}"}}'.format(now.isoformat()).encode('UTF-8'))
self.assertEqual(result, '{"a": "%s"}' % now.isoformat())

def test_with_custom_adapter2(self):
request = testing.DummyRequest()
Expand All @@ -54,7 +54,7 @@ def adapter(obj, req):
now = datetime.utcnow()
renderer = self._makeOne(adapters=((datetime, adapter),))
result = renderer(None)({'a':now}, {'request':request})
self.assertEqual(result, '{{"a": "{0}"}}'.format(now.isoformat()).encode('UTF-8'))
self.assertEqual(result, '{"a": "%s"}' % now.isoformat())

def test_with_custom_serializer(self):
class Serializer(object):
Expand All @@ -66,7 +66,7 @@ def __call__(self, obj, **kw):
renderer = self._makeOne(serializer=serializer, baz=5)
obj = {'a':'b'}
result = renderer(None)(obj, {})
self.assertEqual(result, b'foo')
self.assertEqual(result, 'foo')
self.assertEqual(serializer.obj, obj)
self.assertEqual(serializer.kw['baz'], 5)
self.assertTrue('default' in serializer.kw)
Expand All @@ -84,7 +84,7 @@ def __json__(self, req):
objects = [MyObject(1), MyObject(2)]
renderer = self._makeOne()(None)
result = renderer(objects, {'request':request})
self.assertEqual(result, b'[{"x": 1}, {"x": 2}]')
self.assertEqual(result, '[{"x": 1}, {"x": 2}]')

def test_with_object_adapter_no___json__(self):
class MyObject(object):
Expand Down Expand Up @@ -290,19 +290,6 @@ def test__make_response_result_is_str(self):
response = helper._make_response(la.encode('utf-8'), request)
self.assertEqual(response.body, la.encode('utf-8'))

def test__make_response_result_is_str_no_charset(self):
from pyramid.response import Response
request = testing.DummyRequest()
request.response = Response(content_type='application/json', charset=None)

self.assertIsNone(request.response.charset)

helper = self._makeOne('loo.foo')
la = text_('/La Pe\xc3\xb1a', 'utf-8')
response = helper._make_response(la, request)
self.assertIsNone(response.charset)
self.assertEqual(response.body, la.encode('utf-8'))

def test__make_response_result_is_iterable(self):
from pyramid.response import Response
request = testing.DummyRequest()
Expand Down Expand Up @@ -505,7 +492,7 @@ def test_response_preserved(self):
request.response = response
# use a json renderer, which will mutate the response
result = self._callFUT('json', dict(a=1), request=request)
self.assertEqual(result, b'{"a": 1}')
self.assertEqual(result, '{"a": 1}')
self.assertEqual(request.response, response)

def test_no_response_to_preserve(self):
Expand All @@ -520,7 +507,7 @@ def response(self):
request = DummyRequestWithClassResponse()
# use a json renderer, which will mutate the response
result = self._callFUT('json', dict(a=1), request=request)
self.assertEqual(result, b'{"a": 1}')
self.assertEqual(result, '{"a": 1}')
self.assertFalse('response' in request.__dict__)

class Test_render_to_response(unittest.TestCase):
Expand Down Expand Up @@ -640,7 +627,7 @@ def test_render_to_jsonp(self):
request = testing.DummyRequest()
request.GET['callback'] = 'callback'
result = renderer({'a':'1'}, {'request':request})
self.assertEqual(result, b'/**/callback({"a": "1"});')
self.assertEqual(result, '/**/callback({"a": "1"});')
self.assertEqual(request.response.content_type,
'application/javascript')

Expand All @@ -650,7 +637,7 @@ def test_render_to_jsonp_with_dot(self):
request = testing.DummyRequest()
request.GET['callback'] = 'angular.callbacks._0'
result = renderer({'a':'1'}, {'request':request})
self.assertEqual(result, b'/**/angular.callbacks._0({"a": "1"});')
self.assertEqual(result, '/**/angular.callbacks._0({"a": "1"});')
self.assertEqual(request.response.content_type,
'application/javascript')

Expand Down