summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2012-03-29 04:21:30 -0400
committerChris McDonough <chrism@plope.com>2012-03-29 04:21:30 -0400
commitde797c4cefb03f16cfe3505c85d94c0af24eb066 (patch)
tree226eca892311341011e5cdcaf5e4861e65e9d6ae
parentba60524b56a639ecad42f85b63af2120d9d96cdc (diff)
downloadpyramid-de797c4cefb03f16cfe3505c85d94c0af24eb066.tar.gz
pyramid-de797c4cefb03f16cfe3505c85d94c0af24eb066.tar.bz2
pyramid-de797c4cefb03f16cfe3505c85d94c0af24eb066.zip
- Coverage and docs updates for custom JSON class.
- Fork point: master now represents a future 1.4 release.
-rw-r--r--CHANGES.txt21
-rw-r--r--docs/narr/renderers.rst68
-rw-r--r--pyramid/renderers.py47
-rw-r--r--pyramid/tests/test_renderers.py12
4 files changed, 108 insertions, 40 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 859dc7b74..0714f6940 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,22 +1,13 @@
Next release
============
-Bug Fixes
----------
+Features
+--------
-- Add ``REMOTE_ADDR`` to the ``prequest`` WSGI environ dict for benefit of
- the debug toolbar, which effectively requires it to be present to work
- properly.
-
-- When an asset specification was used as a Mako ``renderer=`` argument and a
- ``mako.modules_directory`` was specified, Pyramid would fail to render the
- template and instead would raise an error when attempting to write the file
- to the modules directory. Example symptom: ``WindowsError: [Error 267] The
- directory name is invalid:
- 'c:\\docume~1\\chrism\\locals~1\\temp\\tmp9jtjix\\pyramid.tests:fixtures'``.
- We now replace the colon in the Mako module filename with a dollar sign, so
- it can work on Windows. See https://github.com/Pylons/pyramid/issues/512
- for more information.
+- Custom objects can be made easily JSON-serializable in Pyramid by defining
+ a ``__json__`` method on the object's class. This method should return
+ values natively serializable by ``json.dumps`` (such as ints, lists,
+ dictionaries, strings, and so forth).
1.3 (2012-03-21)
================
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 52e97d091..34bee3c7f 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -207,17 +207,13 @@ 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`. 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.
-
-Custom objects can be easily serialized by defining a :func:`__json__` method
-on the object. This method should return values serializable by
-:func:`json_dumps`. By defining this method and using a :term:`JSON`
-renderer the :class:`pyramid.renderers.ObjectJSONEncoder` class will be used
-for encoding your object. If you later define your own custom encoder it will
-override :class:`pyramid.renderers.ObjectJSONEncoder`.
+values serializable by ``json.dumps``.
+
+.. note::
+
+ Extra arguments can be passed to ``json.dumps`` by overriding the default
+ ``json`` 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
@@ -235,13 +231,57 @@ 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`.
+.. _json_serializing_custom_objects:
+
+Serializing Custom Objects
+++++++++++++++++++++++++++
+
+Custom objects can be made easily JSON-serializable in Pyramid by defining a
+``__json__`` method on the object's class. This method should return values
+natively serializable by ``json.dumps`` (such as ints, lists, dictionaries,
+strings, and so forth).
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import view_config
+
+ class MyObject(object):
+ def __init__(self, x):
+ self.x = x
+
+ def __json__(self):
+ return {'x':self.x}
+
+ @view_config(renderer='json')
+ def objects(request):
+ return [MyObject(1), MyObject(2)]
+
+ # the JSON value returned by ``objects`` will be:
+ # [{"x": 1}, {"x": 2}]
+
+.. note::
+
+ Honoring the ``__json__`` method of custom objects is a feature new in
+ Pyramid 1.4.
+
+.. warning::
+
+ The machinery which performs the ``__json__`` method-calling magic is in
+ the :class:`pyramid.renderers.ObjectJSONEncoder` class. This class will
+ be used for encoding any non-basic Python object when you use the default
+ ```json`` or ``jsonp`` renderers. But if you later define your own custom
+ JSON renderer and pass it a "cls" argument signifying a different encoder,
+ the encoder you pass will override Pyramid's use of
+ :class:`pyramid.renderers.ObjectJSONEncoder`.
+
.. index::
pair: renderer; JSONP
.. _jsonp_renderer:
JSONP Renderer
---------------
+~~~~~~~~~~~~~~
.. note:: This feature is new in Pyramid 1.1.
@@ -306,6 +346,10 @@ The string ``callback=?`` above in the the ``url`` param to the JQuery
a JSONP request; the ``callback`` parameter will be automatically filled
in for you and used.
+The same custom-object serialization scheme defined used for a "normal" JSON
+renderer in :ref:`json_serializing_custom_objects` can be used when passing
+values to a JSONP renderer too.
+
.. index::
pair: renderer; chameleon
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 859fd3953..0adadf726 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -158,21 +158,31 @@ def string_renderer_factory(info):
return _render
class ObjectJSONEncoder(json.JSONEncoder):
- """ Encoder that is used by :class:`pyramid.renderers.JSON` and
- :class:`pyramid.renderers.JSONP`.
+ """ The default JSON object encoder (a subclass of json.Encoder) used by
+ :class:`pyramid.renderers.JSON` and :class:`pyramid.renderers.JSONP`. It
+ is used when an object returned from a view and presented to a JSON-based
+ renderer is not a builtin Python type otherwise serializable to JSON.
- This encoder will look for a :meth:`__json__` method on an object
- and use the return value when encoding the object to json
- using :meth:`json_dumps`.
+ This ``json.Encoder`` subclass overrides the ``json.Encoder.default``
+ method. The overridden method looks for a ``__json__`` attribute on the
+ object it is passed. If it's found, the encoder will assume it's
+ callable, and will call it with no arguments to obtain a value. The
+ overridden ``default`` method will then return that value (which must be
+ a JSON-serializable basic Python type).
+
+ If the object passed to the overridden ``default`` method has no
+ ``__json__`` attribute, the ``json.JSONEncoder.default`` method is called
+ with the object that it was passed (which will end up raising a
+ :exc:`TypeError`, as it would with any other unserializable type).
This class will be used only when you set a JSON or JSONP
renderer and you do not define your own custom encoder class.
- .. note:: This feature is new in Pyramid 1.3.
+ .. note:: This feature is new in Pyramid 1.4.
"""
def default(self, obj):
- if hasattr(obj, '__json__') and callable(obj.__json__):
+ if hasattr(obj, '__json__'):
return obj.__json__()
return json.JSONEncoder.default(self, obj)
@@ -203,9 +213,12 @@ class JSON(object):
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.
+ .. note::
+
+ This feature is new in Pyramid 1.4. Prior to 1.4 there was
+ no public API for supplying options to the underlying
+ :func:`json.dumps` without defining a custom renderer.
+
"""
def __init__(self, **kw):
@@ -254,6 +267,20 @@ class JSONP(JSON):
config = Configurator()
config.add_renderer('jsonp', JSONP(param_name='callback'))
+ The class also accepts arbitrary keyword arguments; all keyword arguments
+ except ``param_name`` are passed to the ``json.dumps`` function as
+ keyword arguments:
+
+ .. code-block:: python
+
+ from pyramid.config import Configurator
+
+ config = Configurator()
+ config.add_renderer('jsonp', JSONP(param_name='callback', indent=4))
+
+ .. note:: The ability of this class to accept a ``**kw`` in its
+ constructor is new as of Pyramid 1.4.
+
Once this renderer is registered via
:meth:`~pyramid.config.Configurator.add_renderer` as above, you can use
``jsonp`` as the ``renderer=`` parameter to ``@view_config`` or
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 9d9e608f1..f03c7acda 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -374,9 +374,7 @@ class TestJSON(unittest.TestCase):
from json import JSONEncoder
class MyEncoder(JSONEncoder):
def default(self, obj):
- if isinstance(obj, datetime):
- return obj.isoformat()
- return super(JSONEncoder, self).default(obj)
+ return obj.isoformat()
now = datetime.utcnow()
renderer = self._makeOne(cls=MyEncoder)(None)
result = renderer({'a':now}, {})
@@ -394,6 +392,14 @@ class TestJSON(unittest.TestCase):
result = renderer(objects, {})
self.assertEqual(result, '[{"x": 1}, {"x": 2}]')
+ def test_with_object_encoder_no___json__(self):
+ class MyObject(object):
+ def __init__(self, x):
+ self.x = x
+ objects = [MyObject(1), MyObject(2)]
+ renderer = self._makeOne()(None)
+ self.assertRaises(TypeError, renderer, objects, {})
+
class Test_string_renderer_factory(unittest.TestCase):
def _callFUT(self, name):
from pyramid.renderers import string_renderer_factory