From dd9859fb7e93875f246059f2bc6b18cb10dbeff9 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 01:48:30 -0400 Subject: sketch out a subrequest API --- pyramid/router.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/pyramid/router.py b/pyramid/router.py index 0cbe00f3a..ffc67eae5 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -53,6 +53,7 @@ class Router(object): tweens = q(ITweens) if tweens is None: tweens = excview_tween_factory + self.orig_handle_request = self.handle_request self.handle_request = tweens(self.handle_request, registry) self.root_policy = self.root_factory # b/w compat self.registry = registry @@ -161,35 +162,35 @@ class Router(object): return response - def __call__(self, environ, start_response): - """ - Accept ``environ`` and ``start_response``; create a - :term:`request` and route the request to a :app:`Pyramid` - view based on introspection of :term:`view configuration` - within the application registry; call ``start_response`` and - return an iterable. - """ + def subrequest(self, request, use_tweens=False): registry = self.registry has_listeners = self.registry.has_listeners notify = self.registry.notify - request = self.request_factory(environ) threadlocals = {'registry':registry, 'request':request} manager = self.threadlocal_manager manager.push(threadlocals) request.registry = registry + request.subrequest = self.subrequest + if use_tweens: + handle_request = self.handle_request + else: + handle_request = self.orig_handle_request try: try: extensions = self.request_extensions if extensions is not None: request._set_extensions(extensions) - response = self.handle_request(request) + response = handle_request(request) has_listeners and notify(NewResponse(request, response)) if request.response_callbacks: request._process_response_callbacks(response) - return response(request.environ, start_response) + # XXX before subrequest factoring, the below line + # actually invoked Response.__call__, passing it + # the start_response + return response finally: if request.finished_callbacks: @@ -197,3 +198,16 @@ class Router(object): finally: manager.pop() + + def __call__(self, environ, start_response): + """ + Accept ``environ`` and ``start_response``; create a + :term:`request` and route the request to a :app:`Pyramid` + view based on introspection of :term:`view configuration` + within the application registry; call ``start_response`` and + return an iterable. + """ + request = self.request_factory(environ) + response = self.subrequest(request, use_tweens=True) + return response(request.environ, start_response) + -- cgit v1.2.3 From 277b2af26871a4d84730b6d3e38fbaa2f4102823 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 02:33:39 -0400 Subject: add docs --- docs/api/request.rst | 43 +++++++++++++++++++++++++++++++++++++++++++ pyramid/router.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/docs/api/request.rst b/docs/api/request.rst index 1112ea069..5e52cfd50 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -161,6 +161,49 @@ request, the value of this attribute will be ``None``. See :ref:`matched_route`. + .. method:: subrequest(request, use_tweens=False) + + Obtain a response object from the Pyramid application based on + information in the ``request`` object provided. The ``request`` object + must be an object that implements the Pyramid request interface (such + as a :class:`pyramid.request.Request` instance). If ``use_tweens`` is + ``True``, the request will be sent to the :term:`tween` in the tween + stack closest to the request ingress. If ``use_tweens`` is ``False``, + the request will be sent to the main router handler, and no tweens will + be invoked. This isn't *actually* a method of the Request object; it's + a callable added when the Pyramid router is invoked, or when a + subrequest is invoked. This function also: + + - manages the threadlocal stack (so that + :func:`~pyramid.threadlocal.get_current_request` and + :func:`~pyramid.threadlocal.get_current_registry` work during a + request) + + - Adds a ``registry`` attribute (the current Pyramid registry) and a + ``subrequest`` attribute (a callable) to the request object it's + handed. + + - sets request extensions (such as those added via + :meth:`~pyramid.config.Configurator.add_request_method` or + :meth:`~pyramid.config.Configurator.set_request_property`) on the + request it's passed. + + - causes a :class:`~pyramid.event.NewRequest` event to be sent at the + beginning of request processing. + + - causes a :class:`~pyramid.event.ContextFound` event to be sent + when a context resource is found. + + - causes a :class:`~pyramid.event.NewResponse` event to be sent when + the Pyramid application returns a response. + + - Calls any :term:`response callback` functions defined within the + request's lifetime if a response is obtained from the Pyramid + application. + + - Calls any :term:`finished callback` functions defined within the + request's lifetime. + .. automethod:: add_response_callback .. automethod:: add_finished_callback diff --git a/pyramid/router.py b/pyramid/router.py index ffc67eae5..380c9fe30 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -163,6 +163,45 @@ class Router(object): return response def subrequest(self, request, use_tweens=False): + """ + Obtain a response object from the Pyramid application based on + information in the ``request`` object provided. The ``request`` + object must be an object that implements the Pyramid request + interface (such as a :class:`pyramid.request.Request` instance). If + ``use_tweens`` is ``True``, the request will be sent to the + :term:`tween` in the tween stack closest to the request ingress. If + ``use_tweens`` is ``False``, the request will be sent to the main + router handler, and no tweens will be invoked. This function also: + + - manages the threadlocal stack (so that + :func:`~pyramid.threadlocal.get_current_request` and + :func:`~pyramid.threadlocal.get_current_registry` work during a + request) + + - Adds a ``registry`` attribute and a ``subrequest`` attribute to the + request object it's handed. + + - sets request extensions (such as those added via + :meth:`~pyramid.config.Configurator.add_request_method` or + :meth:`~pyramid.config.Configurator.set_request_property`) on the + request it's passed. + + - causes a :class:`~pyramid.event.NewRequest` event to be sent at the + beginning of request processing. + + - causes a :class:`~pyramid.event.ContextFound` event to be sent + when a context resource is found. + + - causes a :class:`~pyramid.event.NewResponse` event to be sent when + the Pyramid application returns a response. + + - Calls any :term:`response callback` functions defined within the + request's lifetime if a response is obtained from the Pyramid + application. + + - Calls any :term:`finished callback` functions defined within the + request's lifetime. + """ registry = self.registry has_listeners = self.registry.has_listeners notify = self.registry.notify -- cgit v1.2.3 From 37d2c224b804dfebe9ee217c7a536364eacdee15 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 03:25:17 -0400 Subject: docs and test --- CHANGES.txt | 6 ++ docs/api/request.rst | 6 ++ docs/index.rst | 1 + docs/latexindex.rst | 1 + docs/narr/subrequest.rst | 143 +++++++++++++++++++++++++++ docs/whatsnew-1.4.rst | 10 ++ pyramid/router.py | 4 +- pyramid/tests/pkgs/subrequestapp/__init__.py | 20 ++++ pyramid/tests/test_integration.py | 55 +++++++---- 9 files changed, 225 insertions(+), 21 deletions(-) create mode 100644 docs/narr/subrequest.rst create mode 100644 pyramid/tests/pkgs/subrequestapp/__init__.py diff --git a/CHANGES.txt b/CHANGES.txt index 6c210600c..6736f6d3e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -163,6 +163,9 @@ Features - Added the ``pyramid.testing.testConfig`` context manager, which can be used to generate a configurator in a test, e.g. ``with testing.testConfig(...):``. +- Users can now invoke a subrequest from within view code using the + ``request.subrequest`` API. + Deprecations ------------ @@ -257,6 +260,9 @@ Documentation how to show Pyramid-generated deprecation warnings while running tests and while running a server. +- Added a "Invoking a Subrequest" chapter to the documentation. It describes + how to use the new ``request.subrequest`` API. + Dependencies ------------ diff --git a/docs/api/request.rst b/docs/api/request.rst index 5e52cfd50..603807abe 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -163,6 +163,10 @@ .. method:: subrequest(request, use_tweens=False) + .. warning:: + + This API was added in Pyramid 1.4a1. + Obtain a response object from the Pyramid application based on information in the ``request`` object provided. The ``request`` object must be an object that implements the Pyramid request interface (such @@ -204,6 +208,8 @@ - Calls any :term:`finished callback` functions defined within the request's lifetime. + See also :ref:`subrequest_chapter`. + .. automethod:: add_response_callback .. automethod:: add_finished_callback diff --git a/docs/index.rst b/docs/index.rst index 4a9b3951b..faf8258c1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -83,6 +83,7 @@ Narrative documentation in chapter form explaining how to use narr/traversal narr/security narr/hybrid + narr/subrequest narr/hooks narr/introspector narr/extending diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 604e6e7c6..6bb875f73 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -54,6 +54,7 @@ Narrative Documentation narr/traversal narr/security narr/hybrid + narr/subrequest narr/hooks narr/introspector narr/extending diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst new file mode 100644 index 000000000..6ed679579 --- /dev/null +++ b/docs/narr/subrequest.rst @@ -0,0 +1,143 @@ +.. index:: + single: subrequest + +.. _subrequest_chapter: + +Invoking a Subrequest +===================== + +.. warning:: + + This feature was added in Pyramid 1.4a1. + +:app:`Pyramid` allows you to invoke a subrequest at any point during the +processing of a request. Invoking a subrequest allows you to obtain a +:term:`response` object from a view callable within your :app:`Pyramid` +application while you're executing a different view callable within the same +application. + +Here's an example application which uses a subrequest: + +.. code-block:: python + + from wsgiref.simple_server import make_server + from pyramid.config import Configurator + from pyramid.request import Request + + def view_one(request): + subreq = Request.blank('/view_two') + response = request.subrequest(subreq) + return response + + def view_two(request): + request.response.body = 'This came from view_two' + return request.response + + if __name__ == '__main__': + config = Configurator() + config.add_route('one', '/view_one') + config.add_route('two', '/view_two') + config.add_view(view_one, route_name='one') + config.add_view(view_two, route_name='two') + app = config.make_wsgi_app() + server = make_server('0.0.0.0', 8080, app) + server.serve_forever() + +When ``/view_one`` is visted in a browser, the text printed in the browser +pane will be ``This came from view_two``. The ``view_one`` view used the +:meth:`pyramid.request.Request.subrequest` API to obtain a response from +another view (``view_two``) within the same application when it executed. It +did so by constructing a new request that had a URL that it knew would match +the ``view_two`` view registration, and passed that new request along to +:meth:`pyramid.request.Request.subrequest`. The ``view_two`` view callable +was invoked, and it returned a response. The ``view_one`` view callable then +simply returned the response it obtained from the ``view_two`` view callable. + +Note that it doesn't matter if the view callable invoked via a subrequest +actually returns a literal Response object. Any view callable that uses a +renderer or which returns an object that can be interpreted by a response +adapter will work too: + +.. code-block:: python + + from wsgiref.simple_server import make_server + from pyramid.config import Configurator + from pyramid.request import Request + + def view_one(request): + subreq = Request.blank('/view_two') + response = request.subrequest(subreq) + return response + + def view_two(request): + return 'This came from view_two' + + if __name__ == '__main__': + config = Configurator() + config.add_route('one', '/view_one') + config.add_route('two', '/view_two') + config.add_view(view_one, route_name='one') + config.add_view(view_two, route_name='two', renderer='string') + app = config.make_wsgi_app() + server = make_server('0.0.0.0', 8080, app) + server.serve_forever() + +Being able to unconditionally obtain a response object by invoking a view +callable indirectly is the main advantage to using +:meth:`pyramid.request.Request.subrequest` instead of simply importing it and +executing it directly. Note that there's not much advantage to invoking a +view using a subrequest if you *can* invoke a view callable directly. It's +much slower to use a subrequest. + +The :meth:`pyramid.request.Request.subrequest` API accepts two arguments: a +positional argument ``request`` that must be provided, and and ``use_tweens`` +keyword argument that is optional; it defaults to ``False``. + +The ``request`` object passed to the API must be an object that implements +the Pyramid request interface (such as a :class:`pyramid.request.Request` +instance). If ``use_tweens`` is ``True``, the request will be sent to the +:term:`tween` in the tween stack closest to the request ingress. If +``use_tweens`` is ``False``, the request will be sent to the main router +handler, and no tweens will be invoked. It's usually best to not invoke any +tweens when executing a subrequest, because the original request will invoke +any tween logic as necessary. The :meth:`pyramid.request.Request.subrequest` +function also: + +- manages the threadlocal stack so that + :func:`~pyramid.threadlocal.get_current_request` and + :func:`~pyramid.threadlocal.get_current_registry` work during a request + (they will return the subrequest instead of the original request) + +- Adds a ``registry`` attribute and a ``subrequest`` attribute to the request + object it's handed. + +- sets request extensions (such as those added via + :meth:`~pyramid.config.Configurator.add_request_method` or + :meth:`~pyramid.config.Configurator.set_request_property`) on the subrequest + object passed as ``request`` + +- causes a :class:`~pyramid.event.NewRequest` event to be sent at the + beginning of request processing. + +- causes a :class:`~pyramid.event.ContextFound` event to be sent when a + context resource is found. + +- causes a :class:`~pyramid.event.NewResponse` event to be sent when the + Pyramid application returns a response. + +- Calls any :term:`response callback` functions defined within the subrequest's + lifetime if a response is obtained from the Pyramid application. + +- Calls any :term:`finished callback` functions defined within the subrequest's + lifetime. + +It's a poor idea to use the original ``request`` object as an argument to +:meth:`~pyramid.request.Request.subrequest`. You should construct a new +request instead as demonstrated in the above example, using +:meth:`pyramid.request.Request.blank`. Once you've constructed a request +object, you'll need to massage the it to match the view callable you'd like +to be executed during the subrequest. This can be done by adjusting the +subrequest's URL, its headers, its request method, and other attributes. See +the documentation for :class:`pyramid.request.Request` to understand how to +massage your new request object into something that will match the view you'd +like to call via a subrequest. diff --git a/docs/whatsnew-1.4.rst b/docs/whatsnew-1.4.rst index 58a44202c..70d5f12a7 100644 --- a/docs/whatsnew-1.4.rst +++ b/docs/whatsnew-1.4.rst @@ -65,6 +65,14 @@ Partial Mako and Chameleon Template Renderings defined as ``macroname`` within the ``template.pt`` template instead of the entire templae. +Subrequest Support +~~~~~~~~~~~~~~~~~~ + +- Developers may invoke a subrequest by using the + :meth:`pyramid.request.Request.subrequest` API. This allows a developer to + obtain a response from one view callable by issuing a subrequest from within + a different view callable. + Minor Feature Additions ----------------------- @@ -239,6 +247,8 @@ Documentation Enhancements how to show Pyramid-generated deprecation warnings while running tests and while running a server. +- Added a :ref:`subrequest_chapter` chapter to the narrative documentation. + - Many cleanups and improvements to narrative and API docs. Dependency Changes diff --git a/pyramid/router.py b/pyramid/router.py index 380c9fe30..75bf70f07 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -201,7 +201,9 @@ class Router(object): - Calls any :term:`finished callback` functions defined within the request's lifetime. - """ + + See also :ref:`subrequest_chapter`. + """ registry = self.registry has_listeners = self.registry.has_listeners notify = self.registry.notify diff --git a/pyramid/tests/pkgs/subrequestapp/__init__.py b/pyramid/tests/pkgs/subrequestapp/__init__.py new file mode 100644 index 000000000..c59993fb7 --- /dev/null +++ b/pyramid/tests/pkgs/subrequestapp/__init__.py @@ -0,0 +1,20 @@ +from pyramid.config import Configurator +from pyramid.request import Request + +def view_one(request): + subreq = Request.blank('/view_two') + response = request.subrequest(subreq) + return response + +def view_two(request): + request.response.body = 'This came from view_two' + return request.response + +def main(): + config = Configurator() + config.add_route('one', '/view_one') + config.add_route('two', '/view_two') + config.add_view(view_one, route_name='one') + config.add_view(view_two, route_name='two') + return config + diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 590ba0760..3f26791de 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -578,26 +578,41 @@ class WSGIApp2AppTest(unittest.TestCase): res = self.testapp.get('/hello', status=200) self.assertTrue(b'Hello' in res.body) -if os.name != 'java': # uses chameleon - class RendererScanAppTest(IntegrationBase, unittest.TestCase): - package = 'pyramid.tests.pkgs.rendererscanapp' - def test_root(self): - res = self.testapp.get('/one', status=200) - self.assertTrue(b'One!' in res.body) - - def test_two(self): - res = self.testapp.get('/two', status=200) - self.assertTrue(b'Two!' in res.body) - - def test_rescan(self): - self.config.scan('pyramid.tests.pkgs.rendererscanapp') - app = self.config.make_wsgi_app() - from webtest import TestApp - testapp = TestApp(app) - res = testapp.get('/one', status=200) - self.assertTrue(b'One!' in res.body) - res = testapp.get('/two', status=200) - self.assertTrue(b'Two!' in res.body) +class SubrequestAppTest(unittest.TestCase): + def setUp(self): + from pyramid.tests.pkgs.subrequestapp import main + config = main() + app = config.make_wsgi_app() + from webtest import TestApp + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + def test_it(self): + res = self.testapp.get('/view_one', status=200) + self.assertTrue(b'This came from view_two' in res.body) + +class RendererScanAppTest(IntegrationBase, unittest.TestCase): + package = 'pyramid.tests.pkgs.rendererscanapp' + def test_root(self): + res = self.testapp.get('/one', status=200) + self.assertTrue(b'One!' in res.body) + + def test_two(self): + res = self.testapp.get('/two', status=200) + self.assertTrue(b'Two!' in res.body) + + def test_rescan(self): + self.config.scan('pyramid.tests.pkgs.rendererscanapp') + app = self.config.make_wsgi_app() + from webtest import TestApp + testapp = TestApp(app) + res = testapp.get('/one', status=200) + self.assertTrue(b'One!' in res.body) + res = testapp.get('/two', status=200) + self.assertTrue(b'Two!' in res.body) class DummyContext(object): pass -- cgit v1.2.3 From 3f3917f9c70ddc83263e440a8bc781bacb33bb6e Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 03:27:00 -0400 Subject: moar --- docs/narr/subrequest.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 6ed679579..5067fa890 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -84,10 +84,10 @@ adapter will work too: Being able to unconditionally obtain a response object by invoking a view callable indirectly is the main advantage to using -:meth:`pyramid.request.Request.subrequest` instead of simply importing it and -executing it directly. Note that there's not much advantage to invoking a -view using a subrequest if you *can* invoke a view callable directly. It's -much slower to use a subrequest. +:meth:`pyramid.request.Request.subrequest` instead of simply importing the +view callable and executing it directly. Note that there's not much +advantage to invoking a view using a subrequest if you *can* invoke a view +callable directly. It's much slower to use a subrequest. The :meth:`pyramid.request.Request.subrequest` API accepts two arguments: a positional argument ``request`` that must be provided, and and ``use_tweens`` @@ -141,3 +141,7 @@ subrequest's URL, its headers, its request method, and other attributes. See the documentation for :class:`pyramid.request.Request` to understand how to massage your new request object into something that will match the view you'd like to call via a subrequest. + +We've demonstrated use of a subrequest from within a view callable, but you +can use the :meth:`~pyramid.request.Request.subrequest` API from within a +tween or an event handler as well. -- cgit v1.2.3 From ab84e1038b9004c978ea49829c2d1b7a2d48d3d4 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 03:30:28 -0400 Subject: explain --- docs/narr/subrequest.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 5067fa890..045cec279 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -82,6 +82,11 @@ adapter will work too: server = make_server('0.0.0.0', 8080, app) server.serve_forever() +Even though the ``view_two`` view callable returned a string, it was invoked +in such a way that the ``string`` renderer associated with the view +registration that was found turned it into a "real" response object for +consumption by ``view_one``. + Being able to unconditionally obtain a response object by invoking a view callable indirectly is the main advantage to using :meth:`pyramid.request.Request.subrequest` instead of simply importing the -- cgit v1.2.3 From fef69b67c5d07f7b7294fe9b52f70fb9de22d17f Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 03:31:40 -0400 Subject: explain --- docs/narr/subrequest.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 045cec279..8429fe7fe 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -92,7 +92,9 @@ callable indirectly is the main advantage to using :meth:`pyramid.request.Request.subrequest` instead of simply importing the view callable and executing it directly. Note that there's not much advantage to invoking a view using a subrequest if you *can* invoke a view -callable directly. It's much slower to use a subrequest. +callable directly. Subrequests are slower and are less convenient if you +actually do want just the literal information returned by a function that +happens to be a view callable. The :meth:`pyramid.request.Request.subrequest` API accepts two arguments: a positional argument ``request`` that must be provided, and and ``use_tweens`` -- cgit v1.2.3 From 1e59ef41026d9754ac9dc21522dd68edfcaf18d7 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 03:37:37 -0400 Subject: garden todo, add docs about exception handling --- TODO.txt | 9 --------- docs/narr/subrequest.rst | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/TODO.txt b/TODO.txt index 202d1afbb..1686c27a2 100644 --- a/TODO.txt +++ b/TODO.txt @@ -82,15 +82,6 @@ Nice-to-Have - Deprecate pyramid.security.view_execution_permitted (it only works for traversal). -- Create a function which performs a recursive request. - -- Create a ``render_view`` that works by using config.derive_view against an - existing view callable instead of querying the registry (some sort of API - for rendering a view callable object to a response from within another view - callable). Possible idea: have config.add_view mark up the - function/method/class like @view_config does, then use the attached info to - derive a view callable whenever called via some API. - - Provide a ``has_view`` function. - Update App engine chapter with less creaky directions. diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 8429fe7fe..39f985520 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -96,6 +96,36 @@ callable directly. Subrequests are slower and are less convenient if you actually do want just the literal information returned by a function that happens to be a view callable. +Note that if a view callable invoked by a subrequest raises an exception, the +exception will usually bubble up to the invoking code: + +.. code-block:: python + + from wsgiref.simple_server import make_server + from pyramid.config import Configurator + from pyramid.request import Request + + def view_one(request): + subreq = Request.blank('/view_two') + response = request.subrequest(subreq) + return response + + def view_two(request): + raise ValueError('foo') + + if __name__ == '__main__': + config = Configurator() + config.add_route('one', '/view_one') + config.add_route('two', '/view_two') + config.add_view(view_one, route_name='one') + config.add_view(view_two, route_name='two', renderer='string') + app = config.make_wsgi_app() + server = make_server('0.0.0.0', 8080, app) + server.serve_forever() + +In the above application, the call to ``request.subrequest(subreq)`` will +raise a :exc:`ValueError` exception instead of obtaining a "500" response. + The :meth:`pyramid.request.Request.subrequest` API accepts two arguments: a positional argument ``request`` that must be provided, and and ``use_tweens`` keyword argument that is optional; it defaults to ``False``. -- cgit v1.2.3 From b895defdcecbf9d758ad92b1bbf6a49f21620c8a Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 03:39:26 -0400 Subject: wording --- docs/narr/subrequest.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 39f985520..4f132f72f 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -124,7 +124,8 @@ exception will usually bubble up to the invoking code: server.serve_forever() In the above application, the call to ``request.subrequest(subreq)`` will -raise a :exc:`ValueError` exception instead of obtaining a "500" response. +actually raise a :exc:`ValueError` exception instead of retrieving a "500" +response from the attempted invocation of ``view_two``. The :meth:`pyramid.request.Request.subrequest` API accepts two arguments: a positional argument ``request`` that must be provided, and and ``use_tweens`` -- cgit v1.2.3 From 64452edb014423054d1bbc0bb3ed8a3e47b6f611 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 03:54:08 -0400 Subject: rename subrequest to invoke_subrequest --- CHANGES.txt | 12 +++++-- docs/api/request.rst | 4 +-- docs/narr/subrequest.rst | 53 ++++++++++++++-------------- docs/whatsnew-1.4.rst | 12 +++++-- pyramid/router.py | 13 +++---- pyramid/tests/pkgs/subrequestapp/__init__.py | 2 +- 6 files changed, 53 insertions(+), 43 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6736f6d3e..66ac42136 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -163,8 +163,8 @@ Features - Added the ``pyramid.testing.testConfig`` context manager, which can be used to generate a configurator in a test, e.g. ``with testing.testConfig(...):``. -- Users can now invoke a subrequest from within view code using the - ``request.subrequest`` API. +- Users can now invoke a subrequest from within view code using a new + ``request.invoke_subrequest`` API. Deprecations ------------ @@ -252,6 +252,12 @@ Backwards Incompatibilities * ``registerSettings``, use ``pyramid.config.Configurator.add_settings`` instead. +- In Pyramid 1.3 and previous, the ``__call__`` method of a Response object + was invoked before any finished callbacks were executed. As of this + release, the ``__call__`` method of a Response object is invoked *after* + finished callbacks are executed. This is in support of the + ``request.invoke_subrequest`` feature. + Documentation ------------- @@ -261,7 +267,7 @@ Documentation while running a server. - Added a "Invoking a Subrequest" chapter to the documentation. It describes - how to use the new ``request.subrequest`` API. + how to use the new ``request.invoke_subrequest`` API. Dependencies ------------ diff --git a/docs/api/request.rst b/docs/api/request.rst index 603807abe..1718d0743 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -161,7 +161,7 @@ request, the value of this attribute will be ``None``. See :ref:`matched_route`. - .. method:: subrequest(request, use_tweens=False) + .. method:: invoke_subrequest(request, use_tweens=False) .. warning:: @@ -184,7 +184,7 @@ request) - Adds a ``registry`` attribute (the current Pyramid registry) and a - ``subrequest`` attribute (a callable) to the request object it's + ``invoke_subrequest`` attribute (a callable) to the request object it's handed. - sets request extensions (such as those added via diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 4f132f72f..bd50b6053 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -26,7 +26,7 @@ Here's an example application which uses a subrequest: def view_one(request): subreq = Request.blank('/view_two') - response = request.subrequest(subreq) + response = request.invoke_subrequest(subreq) return response def view_two(request): @@ -45,13 +45,14 @@ Here's an example application which uses a subrequest: When ``/view_one`` is visted in a browser, the text printed in the browser pane will be ``This came from view_two``. The ``view_one`` view used the -:meth:`pyramid.request.Request.subrequest` API to obtain a response from -another view (``view_two``) within the same application when it executed. It -did so by constructing a new request that had a URL that it knew would match -the ``view_two`` view registration, and passed that new request along to -:meth:`pyramid.request.Request.subrequest`. The ``view_two`` view callable -was invoked, and it returned a response. The ``view_one`` view callable then -simply returned the response it obtained from the ``view_two`` view callable. +:meth:`pyramid.request.Request.invoke_subrequest` API to obtain a response +from another view (``view_two``) within the same application when it +executed. It did so by constructing a new request that had a URL that it +knew would match the ``view_two`` view registration, and passed that new +request along to :meth:`pyramid.request.Request.invoke_subrequest`. The +``view_two`` view callable was invoked, and it returned a response. The +``view_one`` view callable then simply returned the response it obtained from +the ``view_two`` view callable. Note that it doesn't matter if the view callable invoked via a subrequest actually returns a literal Response object. Any view callable that uses a @@ -66,7 +67,7 @@ adapter will work too: def view_one(request): subreq = Request.blank('/view_two') - response = request.subrequest(subreq) + response = request.invoke_subrequest(subreq) return response def view_two(request): @@ -89,8 +90,8 @@ consumption by ``view_one``. Being able to unconditionally obtain a response object by invoking a view callable indirectly is the main advantage to using -:meth:`pyramid.request.Request.subrequest` instead of simply importing the -view callable and executing it directly. Note that there's not much +:meth:`pyramid.request.Request.invoke_subrequest` instead of simply importing +the view callable and executing it directly. Note that there's not much advantage to invoking a view using a subrequest if you *can* invoke a view callable directly. Subrequests are slower and are less convenient if you actually do want just the literal information returned by a function that @@ -107,7 +108,7 @@ exception will usually bubble up to the invoking code: def view_one(request): subreq = Request.blank('/view_two') - response = request.subrequest(subreq) + response = request.invoke_subrequest(subreq) return response def view_two(request): @@ -123,13 +124,13 @@ exception will usually bubble up to the invoking code: server = make_server('0.0.0.0', 8080, app) server.serve_forever() -In the above application, the call to ``request.subrequest(subreq)`` will -actually raise a :exc:`ValueError` exception instead of retrieving a "500" -response from the attempted invocation of ``view_two``. +In the above application, the call to ``request.invoke_subrequest(subreq)`` +will actually raise a :exc:`ValueError` exception instead of retrieving a +"500" response from the attempted invocation of ``view_two``. -The :meth:`pyramid.request.Request.subrequest` API accepts two arguments: a -positional argument ``request`` that must be provided, and and ``use_tweens`` -keyword argument that is optional; it defaults to ``False``. +The :meth:`pyramid.request.Request.invoke_subrequest` API accepts two +arguments: a positional argument ``request`` that must be provided, and and +``use_tweens`` keyword argument that is optional; it defaults to ``False``. The ``request`` object passed to the API must be an object that implements the Pyramid request interface (such as a :class:`pyramid.request.Request` @@ -138,16 +139,16 @@ instance). If ``use_tweens`` is ``True``, the request will be sent to the ``use_tweens`` is ``False``, the request will be sent to the main router handler, and no tweens will be invoked. It's usually best to not invoke any tweens when executing a subrequest, because the original request will invoke -any tween logic as necessary. The :meth:`pyramid.request.Request.subrequest` -function also: +any tween logic as necessary. The +:meth:`pyramid.request.Request.invoke_subrequest` function also: - manages the threadlocal stack so that :func:`~pyramid.threadlocal.get_current_request` and :func:`~pyramid.threadlocal.get_current_registry` work during a request (they will return the subrequest instead of the original request) -- Adds a ``registry`` attribute and a ``subrequest`` attribute to the request - object it's handed. +- Adds a ``registry`` attribute and a ``invoke_subrequest`` attribute (a + callable) to the request object it's handed. - sets request extensions (such as those added via :meth:`~pyramid.config.Configurator.add_request_method` or @@ -170,8 +171,8 @@ function also: lifetime. It's a poor idea to use the original ``request`` object as an argument to -:meth:`~pyramid.request.Request.subrequest`. You should construct a new -request instead as demonstrated in the above example, using +:meth:`~pyramid.request.Request.invoke_subrequest`. You should construct a +new request instead as demonstrated in the above example, using :meth:`pyramid.request.Request.blank`. Once you've constructed a request object, you'll need to massage the it to match the view callable you'd like to be executed during the subrequest. This can be done by adjusting the @@ -181,5 +182,5 @@ massage your new request object into something that will match the view you'd like to call via a subrequest. We've demonstrated use of a subrequest from within a view callable, but you -can use the :meth:`~pyramid.request.Request.subrequest` API from within a -tween or an event handler as well. +can use the :meth:`~pyramid.request.Request.invoke_subrequest` API from +within a tween or an event handler as well. diff --git a/docs/whatsnew-1.4.rst b/docs/whatsnew-1.4.rst index 70d5f12a7..8e333bf5d 100644 --- a/docs/whatsnew-1.4.rst +++ b/docs/whatsnew-1.4.rst @@ -69,9 +69,9 @@ Subrequest Support ~~~~~~~~~~~~~~~~~~ - Developers may invoke a subrequest by using the - :meth:`pyramid.request.Request.subrequest` API. This allows a developer to - obtain a response from one view callable by issuing a subrequest from within - a different view callable. + :meth:`pyramid.request.Request.invoke_subrequest` API. This allows a + developer to obtain a response from one view callable by issuing a subrequest + from within a different view callable. Minor Feature Additions ----------------------- @@ -230,6 +230,12 @@ Backwards Incompatibilities * ``registerSettings``, use :meth:`pyramid.config.Configurator.add_settings` instead. +- In Pyramid 1.3 and previous, the ``__call__`` method of a Response object + returned by a view was invoked before any finished callbacks were executed. + As of this release, the ``__call__`` method of a Response object is invoked + *after* finished callbacks are executed. This is in support of the + :meth:`pyramid.request.Request.invoke_subrequest` feature. + Deprecations ------------ diff --git a/pyramid/router.py b/pyramid/router.py index 75bf70f07..18624376c 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -162,7 +162,7 @@ class Router(object): return response - def subrequest(self, request, use_tweens=False): + def invoke_subrequest(self, request, use_tweens=False): """ Obtain a response object from the Pyramid application based on information in the ``request`` object provided. The ``request`` @@ -178,8 +178,8 @@ class Router(object): :func:`~pyramid.threadlocal.get_current_registry` work during a request) - - Adds a ``registry`` attribute and a ``subrequest`` attribute to the - request object it's handed. + - Adds a ``registry`` attribute and a ``invoke_subrequest`` attribute + (a callable) to the request object it's handed. - sets request extensions (such as those added via :meth:`~pyramid.config.Configurator.add_request_method` or @@ -211,7 +211,7 @@ class Router(object): manager = self.threadlocal_manager manager.push(threadlocals) request.registry = registry - request.subrequest = self.subrequest + request.invoke_subrequest = self.invoke_subrequest if use_tweens: handle_request = self.handle_request else: @@ -228,9 +228,6 @@ class Router(object): if request.response_callbacks: request._process_response_callbacks(response) - # XXX before subrequest factoring, the below line - # actually invoked Response.__call__, passing it - # the start_response return response finally: @@ -249,6 +246,6 @@ class Router(object): return an iterable. """ request = self.request_factory(environ) - response = self.subrequest(request, use_tweens=True) + response = self.invoke_subrequest(request, use_tweens=True) return response(request.environ, start_response) diff --git a/pyramid/tests/pkgs/subrequestapp/__init__.py b/pyramid/tests/pkgs/subrequestapp/__init__.py index c59993fb7..06a4d9d16 100644 --- a/pyramid/tests/pkgs/subrequestapp/__init__.py +++ b/pyramid/tests/pkgs/subrequestapp/__init__.py @@ -3,7 +3,7 @@ from pyramid.request import Request def view_one(request): subreq = Request.blank('/view_two') - response = request.subrequest(subreq) + response = request.invoke_subrequest(subreq) return response def view_two(request): -- cgit v1.2.3