summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--TODO.txt3
-rw-r--r--docs/api/renderers.rst2
-rw-r--r--docs/glossary.rst4
-rw-r--r--docs/narr/hooks.rst6
-rw-r--r--docs/narr/renderers.rst48
-rw-r--r--pyramid/renderers.py84
-rw-r--r--pyramid/tests/test_renderers.py8
-rw-r--r--setup.py2
9 files changed, 83 insertions, 76 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 365e3e455..4b780d3a7 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -127,7 +127,7 @@ Contributors
- Wichert Akkerman, 2011/01/19
-- Christopher Lambacehr, 2011/02/12
+- Christopher Lambacher, 2011/02/12
- Malthe Borch, 2011/02/28
diff --git a/TODO.txt b/TODO.txt
index 544ad0b4e..e0fb0fa27 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -4,6 +4,9 @@ Pyramid TODOs
Nice-to-Have
------------
+- Provide the presumed renderer name to the called view as an attribute of
+ the request.
+
- Have action methods return their discriminators.
- Add docs about upgrading between Pyramid versions (e.g. how to see
diff --git a/docs/api/renderers.rst b/docs/api/renderers.rst
index ab182365e..ea000ad02 100644
--- a/docs/api/renderers.rst
+++ b/docs/api/renderers.rst
@@ -15,8 +15,6 @@
.. autoclass:: JSONP
-.. autoclass:: ObjectJSONEncoder
-
.. attribute:: null_renderer
An object that can be used in advanced integration cases as input to the
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 60920a73a..88598354a 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -290,7 +290,7 @@ Glossary
:term:`principal` (or principals) associated with a request.
WSGI
- `Web Server Gateway Interface <http://wsgi.org/>`_. This is a
+ `Web Server Gateway Interface <http://www.wsgi.org/>`_. This is a
Python standard for connecting web applications to web servers,
similar to the concept of Java Servlets. :app:`Pyramid` requires
that your application be served as a WSGI application.
@@ -299,7 +299,7 @@ Glossary
*Middleware* is a :term:`WSGI` concept. It is a WSGI component
that acts both as a server and an application. Interesting uses
for middleware exist, such as caching, content-transport
- encoding, and other functions. See `WSGI.org <http://wsgi.org>`_
+ encoding, and other functions. See `WSGI.org <http://www.wsgi.org>`_
or `PyPI <http://python.org/pypi>`_ to find middleware for your
application.
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index b6e3dd163..a2143b3c5 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -145,7 +145,7 @@ the view which generates it can be overridden as necessary.
The :term:`forbidden view` callable is a view callable like any other. The
:term:`view configuration` which causes it to be a "forbidden" view consists
-of using the meth:`pyramid.config.Configurator.add_forbidden_view` API or the
+of using the :meth:`pyramid.config.Configurator.add_forbidden_view` API or the
:class:`pyramid.view.forbidden_view_config` decorator.
For example, you can add a forbidden view by using the
@@ -171,7 +171,7 @@ as a forbidden view:
from pyramid.view import forbidden_view_config
- forbidden_view_config()
+ @forbidden_view_config()
def forbidden(request):
return Response('forbidden')
@@ -625,7 +625,7 @@ converts the arbitrary return value into something that implements
:class:`~pyramid.interfaces.IResponse`.
For example, if you'd like to allow view callables to return bare string
-objects (without requiring a a :term:`renderer` to convert a string to a
+objects (without requiring a :term:`renderer` to convert a string to a
response object), you can register an adapter which converts the string to a
Response:
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 34bee3c7f..02063a112 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -177,6 +177,8 @@ using the API of the ``request.response`` attribute. See
.. index::
pair: renderer; JSON
+.. _json_renderer:
+
JSON Renderer
~~~~~~~~~~~~~
@@ -223,9 +225,9 @@ You can configure a view to use the JSON renderer by naming ``json`` as the
:linenos:
config.add_view('myproject.views.hello_world',
- name='hello',
- context='myproject.resources.Hello',
- renderer='json')
+ name='hello',
+ 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
@@ -260,20 +262,38 @@ strings, and so forth).
# the JSON value returned by ``objects`` will be:
# [{"x": 1}, {"x": 2}]
-.. note::
+If you aren't the author of the objects being serialized, it won't be
+possible (or at least not reasonable) to add a custom ``__json__`` method to
+to their classes in order to influence serialization. If the object passed
+to the renderer is not a serializable type, and has no ``__json__`` method,
+usually a :exc:`TypeError` will be raised during serialization. You can
+change this behavior by creating a JSON renderer with a "default" function
+which tries to "sniff" at the object, and returns a valid serialization (a
+string) or raises a TypeError if it can't determine what to do with the
+object. A short example follows:
+
+.. code-block:: python
+ :linenos:
- Honoring the ``__json__`` method of custom objects is a feature new in
- Pyramid 1.4.
+ from pyramid.renderers import JSON
-.. warning::
+ def default(obj):
+ if isinstance(obj, datetime.datetime):
+ return obj.isoformat()
+ raise TypeError('%r is not serializable % (obj,))
+
+ json_renderer = JSON(default=default)
+
+ # then during configuration ....
+ config = Configurator()
+ config.add_renderer('json', json_renderer)
+
+See :class:`pyramid.renderers.JSON` and
+:ref:`adding_and_overriding_renderers` for more information.
+
+.. note::
- 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`.
+ Serializing custom objects is a feature new in Pyramid 1.4.
.. index::
pair: renderer; JSONP
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 0adadf726..b393a40a6 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -157,40 +157,11 @@ def string_renderer_factory(info):
return value
return _render
-class ObjectJSONEncoder(json.JSONEncoder):
- """ 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 ``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.4.
- """
-
- def default(self, obj):
- if hasattr(obj, '__json__'):
- return obj.__json__()
- return json.JSONEncoder.default(self, obj)
-
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
+ :meth:`~pyramid.config.Configurator.add_renderer` API at application
startup time:
.. code-block:: python
@@ -198,12 +169,11 @@ class JSON(object):
from pyramid.config import Configurator
config = Configurator()
- config.add_renderer('myjson', JSON(indent=4, cls=MyJSONEncoder))
+ config.add_renderer('myjson', JSON(indent=4))
- Once this renderer is registered via
- :meth:`~pyramid.config.Configurator.add_renderer` as above, you can use
+ Once this renderer is registered as above, you can use
``myjson`` as the ``renderer=`` parameter to ``@view_config`` or
- :meth:`pyramid.config.Configurator.add_view``:
+ :meth:`~pyramid.config.Configurator.add_view``:
.. code-block:: python
@@ -219,12 +189,20 @@ class JSON(object):
no public API for supplying options to the underlying
:func:`json.dumps` without defining a custom renderer.
+ You can pass a ``default`` argument to this class' constructor (which
+ should be a function) to customize what happens when it attempts to
+ serialize types unrecognized by the base ``json`` module. See
+ :ref:`json_serializing_custom_objects` for more information.
"""
def __init__(self, **kw):
- """ Any keyword arguments will be forwarded to
- :func:`json.dumps`.
- """
+ """ Any keyword arguments will be passed to the ``json.dumps``
+ function. A notable exception is the keyword argument ``default``,
+ which is wrapped in a function that sniffs for ``__json__``
+ attributes before it is passed along to ``json.dumps``"""
+ # we wrap the default callback with our own to get __json__ attr
+ # sniffing
+ self._default = kw.pop('default', None)
self.kw = kw
def __call__(self, info):
@@ -238,23 +216,29 @@ class JSON(object):
ct = response.content_type
if ct == response.default_content_type:
response.content_type = 'application/json'
- return self.value_to_json(value)
+ return self._dumps(value)
return _render
- def value_to_json(self, value):
- """ Convert a Python object to a JSON string.
+ def _default_encode(self, obj):
+ if hasattr(obj, '__json__'):
+ return obj.__json__()
+
+ if self._default is not None:
+ return self._default(obj)
+ raise TypeError('%r is not JSON serializable' % (obj,))
+
+ def _dumps(self, obj):
+ """ Encode a Python object to a JSON string.
By default, this uses the :func:`json.dumps` from the stdlib."""
- if not self.kw.get('cls'):
- self.kw['cls'] = ObjectJSONEncoder
- return json.dumps(value, **self.kw)
+ return json.dumps(obj, default=self._default_encode, **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.
+ making cross-domain AJAX requests.
Configure a JSONP renderer using the
:meth:`pyramid.config.Configurator.add_renderer` API at application
@@ -267,9 +251,9 @@ 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:
+ The class' constructor also accepts arbitrary keyword arguments. All
+ keyword arguments except ``param_name`` are passed to the ``json.dumps``
+ function as its keyword arguments.
.. code-block:: python
@@ -281,6 +265,10 @@ class JSONP(JSON):
.. note:: The ability of this class to accept a ``**kw`` in its
constructor is new as of Pyramid 1.4.
+ The arguments passed to this class' constructor mean the same thing as
+ the arguments passed to :class:`pyramid.renderers.JSON` (including
+ ``default``).
+
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
@@ -319,7 +307,7 @@ class JSONP(JSON):
plain-JSON encoded string with content-type ``application/json``"""
def _render(value, system):
request = system['request']
- val = self.value_to_json(value)
+ val = self._dumps(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 f03c7acda..55ed3f7fd 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -371,12 +371,10 @@ class TestJSON(unittest.TestCase):
def test_with_custom_encoder(self):
from datetime import datetime
- from json import JSONEncoder
- class MyEncoder(JSONEncoder):
- def default(self, obj):
- return obj.isoformat()
+ def default(obj):
+ return obj.isoformat()
now = datetime.utcnow()
- renderer = self._makeOne(cls=MyEncoder)(None)
+ renderer = self._makeOne(default=default)(None)
result = renderer({'a':now}, {})
self.assertEqual(result, '{"a": "%s"}' % now.isoformat())
diff --git a/setup.py b/setup.py
index f8c8c41e9..e325b8a29 100644
--- a/setup.py
+++ b/setup.py
@@ -77,7 +77,7 @@ setup(name='pyramid',
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
- "Framework :: Pylons",
+ "Framework :: Pyramid",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: WSGI",
"License :: Repoze Public License",