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 --- docs/narr/subrequest.rst | 143 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 docs/narr/subrequest.rst (limited to 'docs/narr/subrequest.rst') 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. -- 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(-) (limited to 'docs/narr/subrequest.rst') 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(+) (limited to 'docs/narr/subrequest.rst') 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(-) (limited to 'docs/narr/subrequest.rst') 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 --- docs/narr/subrequest.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'docs/narr/subrequest.rst') 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(-) (limited to 'docs/narr/subrequest.rst') 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 --- docs/narr/subrequest.rst | 53 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'docs/narr/subrequest.rst') 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. -- cgit v1.2.3 From 7259e7e9f3d8f8eb70bcf782f622f8613f99a51d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 13:04:14 -0400 Subject: make use_tweens=True the default, add some more tests --- docs/narr/subrequest.rst | 83 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 15 deletions(-) (limited to 'docs/narr/subrequest.rst') diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index bd50b6053..1c26da73a 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -91,14 +91,14 @@ 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.invoke_subrequest` instead of simply importing -the view callable and executing it directly. Note that there's not much +a 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 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: +exception will usually be converted to a response: .. code-block:: python @@ -124,23 +124,69 @@ 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.invoke_subrequest(subreq)`` -will actually raise a :exc:`ValueError` exception instead of retrieving a -"500" response from the attempted invocation of ``view_two``. +Because the exception view handling tween is generally in the tween list, if +we run the above code, the default :term:`exception view` will generate a +"500" error response, which will be returned to us within ``view_one``: a +Python exception will not be raised. 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``. +``use_tweens`` keyword argument that is optional; it defaults to ``True``. 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.invoke_subrequest` function also: +handler, and no tweens will be invoked. + +In the example above, the call to +:meth:`~pyramid.request.Request.invoke_subrequest` will generally always +return a Response object, even when the view it invokes raises an exception, +because it uses the default ``use_tweens=True``. + +We can cause the subrequest to not be run through the tween stack by passing +``use_tweens=False`` to the call to +:meth:`~pyramid.request.Request.invoke_subrequest`, like this: + +.. 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.invoke_subrequest(subreq, use_tweens=False) + 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 case, 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``, because the tween +which invokes an exception view to generate a response is never run. + +This is one of the major differences between specifying the +``use_tweens=True`` and ``use_tweens=False`` arguments to +:meth:`~pyramid.request.Request.invoke_subrequest`. ``use_tweens=True`` may +also imply invoking transaction commit/abort for the logic executed in the +subrequest if you've got ``pyramid_tm`` in the tween list, injecting debug +HTML if you've got ``pyramid_debugtoolbar`` in the tween list, and other +tween-related side effects as defined by your particular tween list. + +The :meth:`~pyramid.request.Request.invoke_subrequest` function also: - manages the threadlocal stack so that :func:`~pyramid.threadlocal.get_current_request` and @@ -176,11 +222,18 @@ 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. +subrequest's URL, its headers, its request method, and other attributes. The +documentation for :class:`pyramid.request.Request` exposes the methods you +should call and attributes you should set on the request you create to +massage it into something that will actually 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.invoke_subrequest` API from -within a tween or an event handler as well. +within a tween or an event handler as well. It's usually a poor idea to +invoke :meth:`~pyramid.request.Request.invoke_subrequest` from within a +tween, because tweens already by definition have access to a function that +will cause a subrequest (they are passed a ``handle`` function), but you can +do it. It's fine to invoke +:meth:`~pyramid.request.Request.invoke_subrequest` from within an event +handler, however. -- cgit v1.2.3 From 0966cfb8bd7ac43f8ce3b6dd903b03ae3bab8ac6 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 13:08:42 -0400 Subject: explain how this works with the exception view --- docs/narr/subrequest.rst | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'docs/narr/subrequest.rst') diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 1c26da73a..43876b45a 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -98,7 +98,8 @@ 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 be converted to a response: +exception will usually be converted to a response if you have a +:term:`exception view` configured: .. code-block:: python @@ -114,20 +115,26 @@ exception will usually be converted to a response: def view_two(request): raise ValueError('foo') + def excview(request): + request.response.body = b'An exception was raised' + request.response.status_int = 500 + 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', renderer='string') + config.add_view(excview, context=Exception) app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever() Because the exception view handling tween is generally in the tween list, if -we run the above code, the default :term:`exception view` will generate a +we run the above code, the ``excview`` :term:`exception view` will generate a "500" error response, which will be returned to us within ``view_one``: a -Python exception will not be raised. +Python exception will not be raised. The :meth:`pyramid.request.Request.invoke_subrequest` API accepts two arguments: a positional argument ``request`` that must be provided, and and @@ -143,7 +150,8 @@ handler, and no tweens will be invoked. In the example above, the call to :meth:`~pyramid.request.Request.invoke_subrequest` will generally always return a Response object, even when the view it invokes raises an exception, -because it uses the default ``use_tweens=True``. +because it uses the default ``use_tweens=True`` and a :term:`exception view` +is configured. We can cause the subrequest to not be run through the tween stack by passing ``use_tweens=False`` to the call to @@ -163,12 +171,18 @@ We can cause the subrequest to not be run through the tween stack by passing def view_two(request): raise ValueError('foo') + def excview(request): + request.response.body = b'An exception was raised' + request.response.status_int = 500 + 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', renderer='string') + config.add_view(excview, context=Exception) app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever() @@ -176,7 +190,8 @@ We can cause the subrequest to not be run through the tween stack by passing In the above case, 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``, because the tween -which invokes an exception view to generate a response is never run. +which invokes an exception view to generate a response is never run, and +therefore ``excview`` is never executed. This is one of the major differences between specifying the ``use_tweens=True`` and ``use_tweens=False`` arguments to -- cgit v1.2.3 From db2a03786ec76f2c6b7eaebb6f1b7c8b844d8c82 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 16 Sep 2012 22:32:26 -0400 Subject: make use_tweens=False the default --- docs/narr/subrequest.rst | 63 ++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 23 deletions(-) (limited to 'docs/narr/subrequest.rst') diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst index 43876b45a..89551ab35 100644 --- a/docs/narr/subrequest.rst +++ b/docs/narr/subrequest.rst @@ -55,9 +55,11 @@ request along to :meth:`pyramid.request.Request.invoke_subrequest`. The 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 +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: +adapter when found and invoked via +:meth:`pyramid.request.Request.invoke_subrequest` will return a Response +object: .. code-block:: python @@ -97,8 +99,9 @@ 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 be converted to a response if you have a +Note that, by default, if a view callable invoked by a subrequest raises an +exception, the exception will be raised to the caller of +:meth:`~pyramid.request.Request.invoke_subrequest` even if you have a :term:`exception view` configured: .. code-block:: python @@ -131,14 +134,16 @@ exception will usually be converted to a response if you have a server = make_server('0.0.0.0', 8080, app) server.serve_forever() -Because the exception view handling tween is generally in the tween list, if -we run the above code, the ``excview`` :term:`exception view` will generate a -"500" error response, which will be returned to us within ``view_one``: a -Python exception will not be raised. +When we run the above code and visit ``/view_one`` in a browser, the +``excview`` :term:`exception view` will *not* be executed. Instead, the call +to :meth:`~pyramid.request.Request.invoke_subrequest` will cause a +:exc:`ValueError` exception to be raised and a response will never be +generated. We can change this behavior; how to do so is described below in +our discussion of the ``use_tweens`` argument. 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 ``True``. +``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` @@ -148,13 +153,16 @@ instance). If ``use_tweens`` is ``True``, the request will be sent to the handler, and no tweens will be invoked. In the example above, the call to -:meth:`~pyramid.request.Request.invoke_subrequest` will generally always -return a Response object, even when the view it invokes raises an exception, -because it uses the default ``use_tweens=True`` and a :term:`exception view` -is configured. - -We can cause the subrequest to not be run through the tween stack by passing -``use_tweens=False`` to the call to +:meth:`~pyramid.request.Request.invoke_subrequest` will always raise an +exception. This is because it's using the default value for ``use_tweens``, +which is ``False``. You can pass ``use_tweens=True`` instead to ensure that +it will convert an exception to a Response if an :term:`exception view` is +configured instead of raising the exception. This because exception views +are called by the exception view :term:`tween` as described in +:ref:`exception_views` when any view raises an exception. + +We can cause the subrequest to be run through the tween stack by passing +``use_tweens=True`` to the call to :meth:`~pyramid.request.Request.invoke_subrequest`, like this: .. code-block:: python @@ -165,7 +173,7 @@ We can cause the subrequest to not be run through the tween stack by passing def view_one(request): subreq = Request.blank('/view_two') - response = request.invoke_subrequest(subreq, use_tweens=False) + response = request.invoke_subrequest(subreq, use_tweens=True) return response def view_two(request): @@ -187,11 +195,11 @@ We can cause the subrequest to not be run through the tween stack by passing server = make_server('0.0.0.0', 8080, app) server.serve_forever() -In the above case, 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``, because the tween -which invokes an exception view to generate a response is never run, and -therefore ``excview`` is never executed. +In the above case, the call to ``request.invoke_subrequest(subreq)`` will not +raise an exception. Instead, it will retrieve a "500" response from the +attempted invocation of ``view_two``, because the tween which invokes an +exception view to generate a response is run, and therefore ``excview`` is +executed. This is one of the major differences between specifying the ``use_tweens=True`` and ``use_tweens=False`` arguments to @@ -201,7 +209,8 @@ subrequest if you've got ``pyramid_tm`` in the tween list, injecting debug HTML if you've got ``pyramid_debugtoolbar`` in the tween list, and other tween-related side effects as defined by your particular tween list. -The :meth:`~pyramid.request.Request.invoke_subrequest` function also: +The :meth:`~pyramid.request.Request.invoke_subrequest` function also +unconditionally: - manages the threadlocal stack so that :func:`~pyramid.threadlocal.get_current_request` and @@ -222,6 +231,9 @@ The :meth:`~pyramid.request.Request.invoke_subrequest` function also: - causes a :class:`~pyramid.event.ContextFound` event to be sent when a context resource is found. +- Ensures that the user implied by the request passed has the necessary + authorization to invoke view callable before calling it. + - causes a :class:`~pyramid.event.NewResponse` event to be sent when the Pyramid application returns a response. @@ -231,6 +243,11 @@ The :meth:`~pyramid.request.Request.invoke_subrequest` function also: - Calls any :term:`finished callback` functions defined within the subrequest's lifetime. +The invocation of a subrequest has more or less exactly the same effect as +the invocation of a request received by the Pyramid router from a web client +when ``use_tweens=True``. When ``use_tweens=False``, the tweens are skipped +but all the other steps take place. + It's a poor idea to use the original ``request`` object as an argument to :meth:`~pyramid.request.Request.invoke_subrequest`. You should construct a new request instead as demonstrated in the above example, using -- cgit v1.2.3