summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt6
-rw-r--r--docs/api/renderers.rst2
-rw-r--r--docs/narr/renderers.rst68
-rw-r--r--docs/whatsnew-1.1.rst3
-rw-r--r--pyramid/renderers.py68
-rw-r--r--pyramid/tests/test_renderers.py25
6 files changed, 171 insertions, 1 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 1f0549895..9bf8197ab 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -10,6 +10,12 @@ Bug Fixes
tests which use DummyRequest instead of a "real" request, so they know
things are deprecated without necessarily needing a functional test suite.
+Features
+--------
+
+- Add JSONP renderer (see "JSONP renderer" in the Renderers chapter of the
+ documentation).
+
1.1a3 (2011-06-26)
==================
diff --git a/docs/api/renderers.rst b/docs/api/renderers.rst
index 459639a46..c13694219 100644
--- a/docs/api/renderers.rst
+++ b/docs/api/renderers.rst
@@ -11,3 +11,5 @@
.. autofunction:: render_to_response
+.. autoclass:: JSONP
+
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 18cc8e539..f329a7af9 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -228,6 +228,74 @@ 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`.
+.. _jsonp_renderer:
+
+JSONP Renderer
+--------------
+
+.. note:: This feature is new in Pyramid 1.1.
+
+:class:`pyramid.renderers.JSONP` is a `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.
+
+Unlike other renderers, a JSONP renderer needs to be configured at startup
+time "by hand". Configure a JSONP renderer using the
+:meth:`pyramid.config.Configurator.add_renderer` method:
+
+.. code-block:: python
+
+ from pyramid.config import Configurator
+
+ config = Configurator()
+ config.add_renderer('jsonp', JSONP(param_name='callback'))
+
+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
+:meth:`pyramid.config.Configurator.add_view``:
+
+.. code-block:: python
+
+ from pyramid.view import view_config
+
+ @view_config(renderer='jsonp')
+ def myview(request):
+ return {'greeting':'Hello world'}
+
+When a view is called that uses a JSONP renderer:
+
+- If there is a parameter in the request's HTTP query string (aka
+ ``request.GET``) that matches the ``param_name`` of the registered JSONP
+ renderer (by default, ``callback``), the renderer will return a JSONP
+ response.
+
+- If there is no callback parameter in the request's query string, the
+ renderer will return a 'plain' JSON response.
+
+Javscript library AJAX functionality will help you make JSONP requests.
+For example, JQuery has a `getJSON function
+<http://api.jquery.com/jQuery.getJSON/>`_, and has equivalent (but more
+complicated) functionality in its `ajax function
+<http://api.jquery.com/jQuery.ajax/>`_.
+
+For example (Javascript):
+
+.. code-block:: javascript
+
+ var api_url = 'http://api.geonames.org/timezoneJSON' +
+ '?lat=38.301733840000004' +
+ '&lng=-77.45869621' +
+ '&username=fred' +
+ '&callback=?';
+ jqhxr = $.getJSON(api_url);
+
+The string ``callback=?`` above in the the ``url`` param to the JQuery
+``getAjax`` function indicates to jQuery that the query should be made as
+a JSONP request; the ``callback`` parameter will be automatically filled
+in for you and used.
+
.. index::
pair: renderer; chameleon
diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst
index 4d7567886..9895858cd 100644
--- a/docs/whatsnew-1.1.rst
+++ b/docs/whatsnew-1.1.rst
@@ -94,6 +94,9 @@ Default HTTP Exception View
Minor Feature Additions
-----------------------
+- A `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer. See
+ :ref:`jsonp_renderer` for more details.
+
- New authentication policy:
:class:`pyramid.authentication.SessionAuthenticationPolicy`, which uses a
session to store credentials.
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 64c522eb4..b201d32c2 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -127,7 +127,6 @@ def get_renderer(renderer_name, package=None):
helper = RendererHelper(name=renderer_name, package=package)
return helper.renderer
-
# concrete renderer factory implementations (also API)
def json_renderer_factory(info):
@@ -154,6 +153,73 @@ def string_renderer_factory(info):
return value
return _render
+class JSONP(object):
+ """ `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.
+
+ Configure a JSONP 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('jsonp', JSONP(param_name='callback'))
+
+ 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
+ :meth:`pyramid.config.Configurator.add_view``:
+
+ .. code-block:: python
+
+ from pyramid.view import view_config
+
+ @view_config(renderer='jsonp')
+ def myview(request):
+ return {'greeting':'Hello world'}
+
+ When a view is called that uses the JSONP renderer:
+
+ - If there is a parameter in the request's HTTP query string that matches
+ the ``param_name`` of the registered JSONP renderer (by default,
+ ``callback``), the renderer will return a JSONP response.
+
+ - If there is no callback parameter in the request's query string, the
+ renderer will return a 'plain' JSON response.
+
+ .. note:: This feature is new in Pyramid 1.1.
+
+ See also: :ref:`jsonp_renderer`.
+ """
+
+ def __init__(self, param_name='callback'):
+ self.param_name = param_name
+
+ def __call__(self, info):
+ """ Returns JSONP-encoded string with content-type
+ ``application/javascript`` if query parameter matching
+ ``self.param_name`` is present in request.GET; otherwise returns
+ plain-JSON encoded string with content-type ``application/json``"""
+ def _render(value, system):
+ request = system['request']
+ val = json.dumps(value)
+ callback = request.GET.get(self.param_name)
+ if callback is None:
+ ct = 'application/json'
+ body = val
+ else:
+ ct = 'application/javascript'
+ body = '%s(%s)' % (callback, val)
+ response = request.response
+ if response.content_type == response.default_content_type:
+ response.content_type = ct
+ return body
+ return _render
+
# utility functions, not API
class ChameleonRendererLookup(object):
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 68369d570..18b4caa61 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -798,6 +798,31 @@ class Test_get_renderer(unittest.TestCase):
result = self._callFUT('abc/def.pt', package=pyramid.tests)
self.assertEqual(result, renderer)
+class TestJSONP(unittest.TestCase):
+ def _makeOne(self, param_name='callback'):
+ from pyramid.renderers import JSONP
+ return JSONP(param_name)
+
+ def test_render_to_jsonp(self):
+ renderer_factory = self._makeOne()
+ renderer = renderer_factory(None)
+ request = testing.DummyRequest()
+ request.GET['callback'] = 'callback'
+ result = renderer({'a':'1'}, {'request':request})
+ self.assertEqual(result, 'callback({"a": "1"})')
+ self.assertEqual(request.response.content_type,
+ 'application/javascript')
+
+ def test_render_to_json(self):
+ renderer_factory = self._makeOne()
+ renderer = renderer_factory(None)
+ request = testing.DummyRequest()
+ result = renderer({'a':'1'}, {'request':request})
+ self.assertEqual(result, '{"a": "1"}')
+ self.assertEqual(request.response.content_type,
+ 'application/json')
+
+
class Dummy:
pass