summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2012-01-10 22:52:24 -0600
committerwwitzel3 <wayne@pieceofpy.com>2012-03-28 11:07:05 -0400
commitd81ea33ac67ac750053acbfd12616db0130de3c8 (patch)
treea8f88343dc1937db789a916f91682fedbc088638
parent2b37513eaf32184ac201e87260a74563a766e245 (diff)
downloadpyramid-d81ea33ac67ac750053acbfd12616db0130de3c8.tar.gz
pyramid-d81ea33ac67ac750053acbfd12616db0130de3c8.tar.bz2
pyramid-d81ea33ac67ac750053acbfd12616db0130de3c8.zip
intermediate commit
-rw-r--r--docs/api/renderers.rst2
-rw-r--r--docs/narr/renderers.rst10
-rw-r--r--pyramid/renderers.py80
-rw-r--r--pyramid/tests/test_renderers.py29
4 files changed, 94 insertions, 27 deletions
diff --git a/docs/api/renderers.rst b/docs/api/renderers.rst
index 312aa0b31..ea000ad02 100644
--- a/docs/api/renderers.rst
+++ b/docs/api/renderers.rst
@@ -11,6 +11,8 @@
.. autofunction:: render_to_response
+.. autoclass:: JSON
+
.. autoclass:: JSONP
.. attribute:: null_renderer
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 76035cbdf..47182c09e 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -177,8 +177,8 @@ using the API of the ``request.response`` attribute. See
.. index::
pair: renderer; JSON
-``json``: JSON Renderer
-~~~~~~~~~~~~~~~~~~~~~~~
+JSON Renderer
+~~~~~~~~~~~~~
The ``json`` renderer renders view callable results to :term:`JSON`. It
passes the return value through the ``json.dumps`` standard library function,
@@ -207,7 +207,10 @@ representing the JSON serialization of the return value:
'{"content": "Hello!"}'
The return value needn't be a dictionary, but the return value must contain
-values serializable by :func:`json.dumps`.
+values serializable by :func:`json.dumps`. Extra arguments can be passed
+to :func:`json.dumps` by overriding the default renderer. See
+:class:`pyramid.renderers.JSON` and
+:ref:`_adding_and_overriding_renderers` for more information.
You can configure a view to use the JSON renderer by naming ``json`` as the
``renderer`` argument of a view configuration, e.g. by using
@@ -221,7 +224,6 @@ You can configure a view to use the JSON renderer by naming ``json`` as the
context='myproject.resources.Hello',
renderer='json')
-
Views which use the JSON renderer can vary non-body response attributes by
using the api of the ``request.response`` attribute. See
:ref:`request_response_attr`.
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 14941c61a..401d1fdd4 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -144,17 +144,6 @@ def get_renderer(renderer_name, package=None):
# concrete renderer factory implementations (also API)
-def json_renderer_factory(info):
- def _render(value, system):
- request = system.get('request')
- if request is not None:
- response = request.response
- ct = response.content_type
- if ct == response.default_content_type:
- response.content_type = 'application/json'
- return json.dumps(value)
- return _render
-
def string_renderer_factory(info):
def _render(value, system):
if not isinstance(value, string_types):
@@ -168,7 +157,67 @@ def string_renderer_factory(info):
return value
return _render
-class JSONP(object):
+class JSON(object):
+ """ Renderer that returns a JSON-encoded string.
+
+ Configure a custom JSON renderer using the
+ :meth:`pyramid.config.Configurator.add_renderer` API at application
+ startup time:
+
+ .. code-block:: python
+
+ from pyramid.config import Configurator
+
+ config = Configurator()
+ config.add_renderer('myjson', JSON(indent=4, cls=MyJSONEncoder))
+
+ Once this renderer is registered via
+ :meth:`~pyramid.config.Configurator.add_renderer` as above, you can use
+ ``myjson`` as the ``renderer=`` parameter to ``@view_config`` or
+ :meth:`pyramid.config.Configurator.add_view``:
+
+ .. code-block:: python
+
+ from pyramid.view import view_config
+
+ @view_config(renderer='myjson')
+ def myview(request):
+ return {'greeting':'Hello world'}
+
+ .. note:: This feature is new in Pyramid 1.3. Prior to 1.3 there was
+ no public API for supplying options to the underlying
+ :func:`json.dumps` without defining a custom renderer.
+ """
+
+ def __init__(self, **kw):
+ """ Any keyword arguments will be forwarded to
+ :func:`json.dumps`.
+ """
+ self.kw = kw
+
+ def __call__(self, info):
+ """ Returns a plain JSON-encoded string with content-type
+ ``application/json``. The content-type may be overridden by
+ setting ``request.response.content_type``."""
+ def _render(value, system):
+ request = system.get('request')
+ if request is not None:
+ response = request.response
+ ct = response.content_type
+ if ct == response.default_content_type:
+ response.content_type = 'application/json'
+ return self.value_to_json(value)
+ return _render
+
+ def value_to_json(self, value):
+ """ Convert a Python object to a JSON string.
+
+ By default, this uses the :func:`json.dumps` from the stdlib."""
+ return json.dumps(value, **self.kw)
+
+json_renderer_factory = JSON() # bw compat
+
+class JSONP(JSON):
""" `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper
which implements a hybrid json/jsonp renderer. JSONP is useful for
making cross-domain AJAX requests.
@@ -210,9 +259,10 @@ class JSONP(object):
See also: :ref:`jsonp_renderer`.
"""
-
- def __init__(self, param_name='callback'):
+
+ def __init__(self, param_name='callback', **kw):
self.param_name = param_name
+ JSON.__init__(self, **kw)
def __call__(self, info):
""" Returns JSONP-encoded string with content-type
@@ -221,7 +271,7 @@ class JSONP(object):
plain-JSON encoded string with content-type ``application/json``"""
def _render(value, system):
request = system['request']
- val = json.dumps(value)
+ val = self.value_to_json(value)
callback = request.GET.get(self.param_name)
if callback is None:
ct = 'application/json'
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index b32e68e25..c2450b875 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -340,35 +340,48 @@ class TestChameleonRendererLookup(unittest.TestCase):
self.assertNotEqual(reg.queryUtility(ITemplateRenderer, name=spec),
None)
-class Test_json_renderer_factory(unittest.TestCase):
+class TestJSON(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
-
- def _callFUT(self, name):
- from pyramid.renderers import json_renderer_factory
- return json_renderer_factory(name)
+
+ def _makeOne(self, **kw):
+ from pyramid.renderers import JSON
+ return JSON(**kw)
def test_it(self):
- renderer = self._callFUT(None)
+ renderer = self._makeOne()(None)
result = renderer({'a':1}, {})
self.assertEqual(result, '{"a": 1}')
def test_with_request_content_type_notset(self):
request = testing.DummyRequest()
- renderer = self._callFUT(None)
+ renderer = self._makeOne()(None)
renderer({'a':1}, {'request':request})
self.assertEqual(request.response.content_type, 'application/json')
def test_with_request_content_type_set(self):
request = testing.DummyRequest()
request.response.content_type = 'text/mishmash'
- renderer = self._callFUT(None)
+ renderer = self._makeOne()(None)
renderer({'a':1}, {'request':request})
self.assertEqual(request.response.content_type, 'text/mishmash')
+ def test_with_custom_encoder(self):
+ from datetime import datetime
+ from json import JSONEncoder
+ class MyEncoder(JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, datetime):
+ return obj.isoformat()
+ return super(JSONEncoder, self).default(obj)
+ now = datetime.utcnow()
+ renderer = self._makeOne(cls=MyEncoder)(None)
+ result = renderer({'a':now}, {})
+ self.assertEqual(result, '{"a": "%s"}' % now.isoformat())
+
class Test_string_renderer_factory(unittest.TestCase):
def _callFUT(self, name):
from pyramid.renderers import string_renderer_factory