summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-05-16 02:11:56 -0400
committerChris McDonough <chrism@plope.com>2011-05-16 02:11:56 -0400
commit1ffb8e3cc21603b29ccd78152f82cca7f61a09b1 (patch)
treea58614dbc7ccbd49d2f8a744e2c5aea01b24e444
parent2ce65257cce304bd0f14d3b1bd4fd83ab957398b (diff)
downloadpyramid-1ffb8e3cc21603b29ccd78152f82cca7f61a09b1.tar.gz
pyramid-1ffb8e3cc21603b29ccd78152f82cca7f61a09b1.tar.bz2
pyramid-1ffb8e3cc21603b29ccd78152f82cca7f61a09b1.zip
- Added API docs for ``pyramid.httpexceptions.abort`` and
``pyramid.httpexceptions.redirect``. - Added "HTTP Exceptions" section to Views narrative chapter including a description of ``pyramid.httpexceptions.abort``; adjusted redirect section to note ``pyramid.httpexceptions.redirect``. - A default exception view for the context ``webob.exc.HTTPException`` (aka ``pyramid.httpexceptions.HTTPException``) is now registered by default. This means that an instance of any exception class imported from ``pyramid.httpexceptions`` (such as ``HTTPFound``) can now be raised from within view code; when raised, this exception view will render the exception to a response. - New functions named ``pyramid.httpexceptions.abort`` and ``pyramid.httpexceptions.redirect`` perform the equivalent of their Pylons brethren when an HTTP exception handler is registered. These functions take advantage of the newly registered exception view for ``webob.exc.HTTPException``. - The Configurator now accepts an additional keyword argument named ``httpexception_view``. By default, this argument is populated with a default exception view function that will be used when an HTTP exception is raised. When ``None`` is passed for this value, an exception view for HTTP exceptions will not be registered. Passing ``None`` returns the behavior of raising an HTTP exception to that of Pyramid 1.0 (the exception will propagate to middleware and to the WSGI server).
-rw-r--r--CHANGES.txt28
-rw-r--r--TODO.txt4
-rw-r--r--docs/api/httpexceptions.rst4
-rw-r--r--docs/glossary.rst6
-rw-r--r--docs/narr/views.rst249
-rw-r--r--docs/whatsnew-1.1.rst35
-rw-r--r--pyramid/config.py25
-rw-r--r--pyramid/httpexceptions.py33
-rw-r--r--pyramid/tests/test_config.py46
-rw-r--r--pyramid/tests/test_httpexceptions.py79
10 files changed, 432 insertions, 77 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 0992af9ef..756d1345c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -37,6 +37,13 @@ Documentation
- Added "What's New in Pyramid 1.1" to HTML rendering of documentation.
+- Added API docs for ``pyramid.httpexceptions.abort`` and
+ ``pyramid.httpexceptions.redirect``.
+
+- Added "HTTP Exceptions" section to Views narrative chapter including a
+ description of ``pyramid.httpexceptions.abort``; adjusted redirect section
+ to note ``pyramid.httpexceptions.redirect``.
+
Features
--------
@@ -97,6 +104,27 @@ Features
section entitled "Static Routes" in the URL Dispatch narrative chapter for
more information.
+- A default exception view for the context ``webob.exc.HTTPException`` (aka
+ ``pyramid.httpexceptions.HTTPException``) is now registered by default.
+ This means that an instance of any exception class imported from
+ ``pyramid.httpexceptions`` (such as ``HTTPFound``) can now be raised from
+ within view code; when raised, this exception view will render the
+ exception to a response.
+
+- New functions named ``pyramid.httpexceptions.abort`` and
+ ``pyramid.httpexceptions.redirect`` perform the equivalent of their Pylons
+ brethren when an HTTP exception handler is registered. These functions
+ take advantage of the newly registered exception view for
+ ``webob.exc.HTTPException``.
+
+- The Configurator now accepts an additional keyword argument named
+ ``httpexception_view``. By default, this argument is populated with a
+ default exception view function that will be used when an HTTP exception is
+ raised. When ``None`` is passed for this value, an exception view for HTTP
+ exceptions will not be registered. Passing ``None`` returns the behavior
+ of raising an HTTP exception to that of Pyramid 1.0 (the exception will
+ propagate to middleware and to the WSGI server).
+
Bug Fixes
---------
diff --git a/TODO.txt b/TODO.txt
index 0f7d6342c..d85f3b7f0 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -4,10 +4,6 @@ Pyramid TODOs
Should-Have
-----------
-- Consider adding a default exception view for HTTPException and attendant
- ``redirect`` and ``abort`` functions ala Pylons (promised Mike I'd enable
- this in 1.1).
-
- Add narrative docs for wsgiapp and wsgiapp2.
Nice-to-Have
diff --git a/docs/api/httpexceptions.rst b/docs/api/httpexceptions.rst
index 57ca8092c..73da4126b 100644
--- a/docs/api/httpexceptions.rst
+++ b/docs/api/httpexceptions.rst
@@ -5,6 +5,10 @@
.. automodule:: pyramid.httpexceptions
+ .. autofunction:: abort
+
+ .. autofunction:: redirect
+
.. attribute:: status_map
A mapping of integer status code to exception class (eg. the
diff --git a/docs/glossary.rst b/docs/glossary.rst
index e1e9e76a9..797343e5e 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -618,6 +618,12 @@ Glossary
request processing. See :ref:`exception_views` for more
information.
+ HTTP Exception
+ The set of exception classes defined in :mod:`pyramid.httpexceptions`.
+ These can be used to generate responses with various status codes when
+ raised or returned from a :term:`view callable`. See also
+ :ref:`http_exceptions`.
+
thread local
A thread-local variable is one which is essentially a global variable
in terms of how it is accessed and treated, however, each `thread
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index 5c9bd91af..465cd3c0d 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -51,14 +51,14 @@ the request object contains everything your application needs to know
about the specific HTTP request being made.
A view callable's ultimate responsibility is to create a :mod:`Pyramid`
-:term:`Response` object. This can be done by creating the response
-object in the view callable code and returning it directly, as we will
-be doing in this chapter. However, if a view callable does not return a
-response itself, it can be configured to use a :term:`renderer` that
-converts its return value into a :term:`Response` object. Using
-renderers is the common way that templates are used with view callables
-to generate markup. See the :ref:`renderers_chapter` chapter for
-details.
+:term:`Response` object. This can be done by creating the response object in
+the view callable code and returning it directly, as we will be doing in this
+chapter. However, if a view callable does not return a response itself, it
+can be configured to use a :term:`renderer` that converts its return value
+into a :term:`Response` object. Using renderers is the common way that
+templates are used with view callables to generate markup: see the
+:ref:`renderers_chapter` chapter for details. In some cases, a response may
+also be generated by raising an exception within a view callable.
.. index::
single: view calling convention
@@ -234,8 +234,8 @@ You don't need to always use :class:`~pyramid.response.Response` to represent
a response. :app:`Pyramid` provides a range of different "exception" classes
which can act as response objects too. For example, an instance of the class
:class:`pyramid.httpexceptions.HTTPFound` is also a valid response object
-(see :ref:`http_redirect`). A view can actually return any object that has
-the following attributes.
+(see :ref:`http_exceptions` and ref:`http_redirect`). A view can actually
+return any object that has the following attributes.
status
The HTTP status code (including the name) for the response as a string.
@@ -255,46 +255,6 @@ app_iter
These attributes form the structure of the "Pyramid Response interface".
.. index::
- single: view http redirect
- single: http redirect (from a view)
-
-.. _http_redirect:
-
-Using a View Callable to Do an HTTP Redirect
---------------------------------------------
-
-You can issue an HTTP redirect from within a view by returning a particular
-kind of response.
-
-.. code-block:: python
- :linenos:
-
- from pyramid.httpexceptions import HTTPFound
-
- def myview(request):
- return HTTPFound(location='http://example.com')
-
-All exception types from the :mod:`pyramid.httpexceptions` module implement
-the :term:`Response` interface; any can be returned as the response from a
-view. See :mod:`pyramid.httpexceptions` for the documentation for the
-``HTTPFound`` exception; it also includes other response types that imply
-other HTTP response codes, such as ``HTTPUnauthorized`` for ``401
-Unauthorized``.
-
-.. note::
-
- Although exception types from the :mod:`pyramid.httpexceptions` module are
- in fact bona fide Python :class:`Exception` types, the :app:`Pyramid` view
- machinery expects them to be *returned* by a view callable rather than
- *raised*.
-
- It is possible, however, in Python 2.5 and above, to configure an
- *exception view* to catch these exceptions, and return an appropriate
- :class:`~pyramid.response.Response`. The simplest such view could just
- catch and return the original exception. See :ref:`exception_views` for
- more details.
-
-.. index::
single: view exceptions
.. _special_exceptions_in_callables:
@@ -304,13 +264,21 @@ Using Special Exceptions In View Callables
Usually when a Python exception is raised within a view callable,
:app:`Pyramid` allows the exception to propagate all the way out to the
-:term:`WSGI` server which invoked the application.
+:term:`WSGI` server which invoked the application. It is usually caught and
+logged there.
-However, for convenience, two special exceptions exist which are always
-handled by :app:`Pyramid` itself. These are
-:exc:`pyramid.exceptions.NotFound` and :exc:`pyramid.exceptions.Forbidden`.
-Both are exception classes which accept a single positional constructor
-argument: a ``message``.
+However, for convenience, a special set of exceptions exists. When one of
+these exceptions is raised within a view callable, it will always cause
+:app:`Pyramid` to generate a response. Two categories of special exceptions
+exist: internal exceptions and HTTP exceptions.
+
+Internal Exceptions
+~~~~~~~~~~~~~~~~~~~
+
+:exc:`pyramid.exceptions.NotFound` and :exc:`pyramid.exceptions.Forbidden`
+are exceptions often raised by Pyramid itself when it (respectively) cannot
+find a view to service a request or when authorization was forbidden by a
+security policy. However, they can also be raised by application developers.
If :exc:`~pyramid.exceptions.NotFound` is raised within view code, the result
of the :term:`Not Found View` will be returned to the user agent which
@@ -320,22 +288,100 @@ If :exc:`~pyramid.exceptions.Forbidden` is raised within view code, the result
of the :term:`Forbidden View` will be returned to the user agent which
performed the request.
-In all cases, the message provided to the exception constructor is made
-available to the view which :app:`Pyramid` invokes as
+Both are exception classes which accept a single positional constructor
+argument: a ``message``. In all cases, the message provided to the exception
+constructor is made available to the view which :app:`Pyramid` invokes as
``request.exception.args[0]``.
+An example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.exceptions import NotFound
+
+ def aview(request):
+ raise NotFound('not found!')
+
+Internal exceptions may not be *returned* in order to generate a response,
+they must always be *raised*.
+
+.. index::
+ single: HTTP exceptions
+
+.. _http_exceptions:
+
+HTTP Exceptions
+~~~~~~~~~~~~~~~
+
+All exception classes documented in the :mod:`pyramid.httpexceptions` module
+implement the :term:`Response` interface; an instance of any of these classes
+can be returned or raised from within a view. The instance will be used as
+as the view's response.
+
+For example, the :class:`pyramid.httpexceptions.HTTPUnauthorized` exception
+can be raised. This will cause a response to be generated with a ``401
+Unauthorized`` status:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.httpexceptions import HTTPUnauthorized
+
+ def aview(request):
+ raise HTTPUnauthorized()
+
+A shortcut for importing and raising an HTTP exception is the
+:func:`pyramid.httpexceptions.abort` function. This function accepts an HTTP
+status code and raises the corresponding HTTP exception. For example, to
+raise HTTPUnauthorized, instead of the above, you could do:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.httpexceptions import abort
+
+ def aview(request):
+ abort(401)
+
+This is the case because ``401`` is the HTTP status code for "HTTP
+Unauthorized". Therefore, ``abort(401)`` is functionally equivalent to
+``raise HTTPUnauthorized()``. Other exceptions in
+:mod:`pyramid.httpexceptions` can be raised via
+:func:`pyramid.httpexceptions.abort` as well, as long as the status code
+associated with the exception is provided to the function.
+
+An HTTP exception, instead of being raised, can alternately be *returned*
+(HTTP exceptions are also valid response objects):
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.httpexceptions import HTTPUnauthorized
+
+ def aview(request):
+ return HTTPUnauthorized()
+
+Note that :class:`pyramid.exceptions.NotFound` is *not* the same as
+:class:`pyramid.httpexceptions.HTTPNotFound`. If the latter is raised, the
+:term:`Not Found view` will *not* be called automatically. Likewise,
+:class:`pyramid.exceptions.Foribdden` is not the same exception as
+:class:`pyramid.httpexceptions.HTTPForbidden`. If the latter is raised, the
+:term:`Forbidden view` will not be called automatically.
+
.. index::
single: exception views
.. _exception_views:
-Exception Views
----------------
+Custom Exception Views
+----------------------
-The machinery which allows the special :exc:`~pyramid.exceptions.NotFound` and
-:exc:`~pyramid.exceptions.Forbidden` exceptions to be caught by specialized
-views as described in :ref:`special_exceptions_in_callables` can also be used
-by application developers to convert arbitrary exceptions to responses.
+The machinery which allows :exc:`~pyramid.exceptions.NotFound`,
+:exc:`~pyramid.exceptions.Forbidden` and HTTP exceptions to be caught by
+specialized views as described in :ref:`special_exceptions_in_callables` can
+also be used by application developers to convert arbitrary exceptions to
+responses.
To register a view that should be called whenever a particular exception is
raised from with :app:`Pyramid` view code, use the exception class or one of
@@ -359,6 +405,7 @@ raises a ``helloworld.exceptions.ValidationFailure`` exception:
.. code-block:: python
:linenos:
+ from pyramid.view import view_config
from helloworld.exceptions import ValidationFailure
@view_config(context=ValidationFailure)
@@ -380,12 +427,13 @@ exception view registration:
:linenos:
from pyramid.view import view_config
- from pyramid.exceptions import NotFound
- from pyramid.httpexceptions import HTTPNotFound
+ from helloworld.exceptions import ValidationFailure
- @view_config(context=NotFound, route_name='home')
- def notfound_view(request):
- return HTTPNotFound()
+ @view_config(context=ValidationFailure, route_name='home')
+ def failed_validation(exc, request):
+ response = Response('Failed validation: %s' % exc.msg)
+ response.status_int = 500
+ return response
The above exception view names the ``route_name`` of ``home``, meaning that
it will only be called when the route matched has a name of ``home``. You
@@ -407,7 +455,68 @@ exception views which have a name will be ignored.
can use an exception as ``context`` for a normal view.
Exception views can be configured with any view registration mechanism:
-``@view_config`` decorator, ZCML, or imperative ``add_view`` styles.
+``@view_config`` decorator or imperative ``add_view`` styles.
+
+.. index::
+ single: view http redirect
+ single: http redirect (from a view)
+
+.. _http_redirect:
+
+Using a View Callable to Do an HTTP Redirect
+--------------------------------------------
+
+Two methods exist to redirect to another URL from within a view callable: a
+short form and a long form. The short form should be preferred when
+possible.
+
+Short Form
+~~~~~~~~~~
+
+You can issue an HTTP redirect from within a view callable by using the
+:func:`pyramid.httpexceptions.redirect` function. This function raises an
+:class:`pyramid.httpexceptions.HTTPFound` exception (a "302"), which is
+caught by an exception handler and turned into a response.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.httpexceptions import redirect
+
+ def myview(request):
+ redirect('http://example.com')
+
+Long Form
+~~~~~~~~~
+
+You can issue an HTTP redirect from within a view "by hand" instead of
+relying on the :func:`pyramid.httpexceptions.redirect` function to do it for
+you.
+
+To do so, you can *return* a :class:`pyramid.httpexceptions.HTTPFound`
+instance.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.httpexceptions import HTTPFound
+
+ def myview(request):
+ return HTTPFound(location='http://example.com')
+
+Or, alternately, you can *raise* an HTTPFound exception instead of returning
+one.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.httpexceptions import HTTPFound
+
+ def myview(request):
+ raise HTTPFound(location='http://example.com')
+
+The above form of generating a response by raising HTTPFound is completely
+equivalent to ``redirect('http://example.com')``.
.. index::
single: unicode, views, and forms
diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst
index 992e0b637..488328519 100644
--- a/docs/whatsnew-1.1.rst
+++ b/docs/whatsnew-1.1.rst
@@ -18,6 +18,9 @@ The major feature additions in Pyramid 1.1 are:
- Support for "static" routes.
+- Default HTTP exception view and associated ``redirect`` and ``abort``
+ convenience functions.
+
``request.response``
~~~~~~~~~~~~~~~~~~~~
@@ -50,6 +53,31 @@ Static Routes
be useful for URL generation via ``route_url`` and ``route_path``. See the
section entitled :ref:`static_route_narr` for more information.
+Default HTTP Exception View
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- A default exception view for the context :exc:`webob.exc.HTTPException`
+ (aka :class:`pyramid.httpexceptions.HTTPException`) is now registered by
+ default. This means that an instance of any exception class imported from
+ :mod:`pyramid.httpexceptions` (such as ``HTTPFound``) can now be raised
+ from within view code; when raised, this exception view will render the
+ exception to a response.
+
+ New convenience functions named :func:`pyramid.httpexceptions.abort` and
+ :func:`pyramid.httpexceptions.redirect` perform the equivalent of their
+ Pylons brethren when an HTTP exception handler is registered. These
+ functions take advantage of the newly registered exception view for
+ :exc:`webob.exc.HTTPException`.
+
+ To allow for configuration of this feature, the :term:`Configurator` now
+ accepts an additional keyword argument named ``httpexception_view``. By
+ default, this argument is populated with a default exception view function
+ that will be used when an HTTP exception is raised. When ``None`` is
+ passed for this value, an exception view for HTTP exceptions will not be
+ registered. Passing ``None`` returns the behavior of raising an HTTP
+ exception to that of Pyramid 1.0 (the exception will propagate to
+ middleware and to the WSGI server).
+
Minor Feature Additions
-----------------------
@@ -222,3 +250,10 @@ Documentation Enhancements
- Added a section to the "URL Dispatch" narrative chapter regarding the new
"static" route feature entitled :ref:`static_route_narr`.
+
+- Added API docs for :func:`pyramid.httpexceptions.abort` and
+ :func:`pyramid.httpexceptions.redirect`.
+
+- Added :ref:`http_exceptions` section to Views narrative chapter including a
+ description of :func:`pyramid.httpexceptions.abort`` and
+ :func:`pyramid.httpexceptions.redirect`.
diff --git a/pyramid/config.py b/pyramid/config.py
index 4e06a9b2e..85a66a837 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -59,6 +59,8 @@ from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import Forbidden
from pyramid.exceptions import NotFound
from pyramid.exceptions import PredicateMismatch
+from pyramid.httpexceptions import HTTPException
+from pyramid.httpexceptions import default_httpexception_view
from pyramid.i18n import get_localizer
from pyramid.log import make_stream_logger
from pyramid.mako_templating import renderer_factory as mako_renderer_factory
@@ -139,7 +141,8 @@ class Configurator(object):
``package``, ``settings``, ``root_factory``, ``authentication_policy``,
``authorization_policy``, ``renderers`` ``debug_logger``,
``locale_negotiator``, ``request_factory``, ``renderer_globals_factory``,
- ``default_permission``, ``session_factory``, and ``autocommit``.
+ ``default_permission``, ``session_factory``, ``default_view_mapper``,
+ ``autocommit``, and ``httpexception_view``.
If the ``registry`` argument is passed as a non-``None`` value, it
must be an instance of the :class:`pyramid.registry.Registry`
@@ -254,7 +257,17 @@ class Configurator(object):
:term:`view mapper` factory for view configurations that don't otherwise
specify one (see :class:`pyramid.interfaces.IViewMapperFactory`). If a
default_view_mapper is not passed, a superdefault view mapper will be
- used. """
+ used.
+
+ If ``httpexception_view`` is passed, it must be a :term:`view callable`
+ or ``None``. If it is a view callable, it will be used as an exception
+ view callable when an :term:`HTTP exception` is raised (any named
+ exception from the ``pyramid.httpexceptions`` module) by
+ :func:`pyramid.httpexceptions.abort`,
+ :func:`pyramid.httpexceptions.redirect` or 'by hand'. If it is ``None``,
+ no httpexception view will be registered. By default, the
+ ``pyramid.httpexceptions.default_httpexception_view`` function is
+ used. This behavior is new in Pyramid 1.1. """
manager = manager # for testing injection
venusian = venusian # for testing injection
@@ -277,6 +290,7 @@ class Configurator(object):
session_factory=None,
default_view_mapper=None,
autocommit=False,
+ httpexception_view=default_httpexception_view,
):
if package is None:
package = caller_package()
@@ -302,6 +316,7 @@ class Configurator(object):
default_permission=default_permission,
session_factory=session_factory,
default_view_mapper=default_view_mapper,
+ httpexception_view=httpexception_view,
)
def _set_settings(self, mapping):
@@ -658,7 +673,8 @@ class Configurator(object):
renderers=DEFAULT_RENDERERS, debug_logger=None,
locale_negotiator=None, request_factory=None,
renderer_globals_factory=None, default_permission=None,
- session_factory=None, default_view_mapper=None):
+ session_factory=None, default_view_mapper=None,
+ httpexception_view=default_httpexception_view):
""" When you pass a non-``None`` ``registry`` argument to the
:term:`Configurator` constructor, no initial 'setup' is performed
against the registry. This is because the registry you pass in may
@@ -690,6 +706,9 @@ class Configurator(object):
self.add_renderer(name, renderer)
self.add_view(default_exceptionresponse_view,
context=IExceptionResponse)
+ if httpexception_view is not None:
+ httpexception_view = self.maybe_dotted(httpexception_view)
+ self.add_view(httpexception_view, context=HTTPException)
if locale_negotiator:
locale_negotiator = self.maybe_dotted(locale_negotiator)
registry.registerUtility(locale_negotiator, ILocaleNegotiator)
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index f56910b53..cbd87520b 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -48,3 +48,36 @@ from webob.exc import HTTPBadGateway
from webob.exc import HTTPServiceUnavailable
from webob.exc import HTTPGatewayTimeout
from webob.exc import HTTPVersionNotSupported
+
+from webob.response import Response
+
+def abort(status_code, **kw):
+ """Aborts the request immediately by raising an HTTP exception. The
+ values in ``*kw`` will be passed to the HTTP exception constructor.
+ Example::
+
+ abort(404) # raises an HTTPNotFound exception.
+ """
+ exc = status_map[status_code](**kw)
+ raise exc.exception
+
+
+def redirect(url, code=302, **kw):
+ """Raises a redirect exception to the specified URL.
+
+ Optionally, a code variable may be passed with the status code of
+ the redirect, ie::
+
+ redirect(route_url('foo', request), code=303)
+
+ """
+ exc = status_map[code]
+ raise exc(location=url, **kw).exception
+
+def default_httpexception_view(context, request):
+ if isinstance(context, Response):
+ # WSGIHTTPException, a Response (2.5+)
+ return context
+ # HTTPException, a WSGI app (2.4)
+ return request.get_response(context)
+
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index 97a93616d..9c8f4875b 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -203,6 +203,38 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(IViewMapperFactory),
mapper)
+ def test_ctor_httpexception_view_default(self):
+ from zope.interface import implementedBy
+ from pyramid.httpexceptions import HTTPException
+ from pyramid.httpexceptions import default_httpexception_view
+ from pyramid.interfaces import IRequest
+ config = self._makeOne()
+ view = self._getViewCallable(config,
+ ctx_iface=implementedBy(HTTPException),
+ request_iface=IRequest)
+ self.failUnless(view is default_httpexception_view)
+
+ def test_ctor_httpexception_view_None(self):
+ from zope.interface import implementedBy
+ from pyramid.httpexceptions import HTTPException
+ from pyramid.interfaces import IRequest
+ config = self._makeOne(httpexception_view=None)
+ view = self._getViewCallable(config,
+ ctx_iface=implementedBy(HTTPException),
+ request_iface=IRequest)
+ self.failUnless(view is None)
+
+ def test_ctor_httpexception_view_custom(self):
+ from zope.interface import implementedBy
+ from pyramid.httpexceptions import HTTPException
+ from pyramid.interfaces import IRequest
+ def httpexception_view(context, request): pass
+ config = self._makeOne(httpexception_view=httpexception_view)
+ view = self._getViewCallable(config,
+ ctx_iface=implementedBy(HTTPException),
+ request_iface=IRequest)
+ self.failUnless(view is httpexception_view)
+
def test_with_package_module(self):
from pyramid.tests import test_configuration
import pyramid.tests
@@ -289,6 +321,20 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(views[0], ((default_exceptionresponse_view,),
{'context':IExceptionResponse}))
+ def test_setup_registry_registers_default_httpexception_view(self):
+ from pyramid.httpexceptions import HTTPException
+ from pyramid.httpexceptions import default_httpexception_view
+ class DummyRegistry(object):
+ def registerUtility(self, *arg, **kw):
+ pass
+ reg = DummyRegistry()
+ config = self._makeOne(reg)
+ views = []
+ config.add_view = lambda *arg, **kw: views.append((arg, kw))
+ config.setup_registry()
+ self.assertEqual(views[1], ((default_httpexception_view,),
+ {'context':HTTPException}))
+
def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self):
from zope.interface import implementedBy
from pyramid.interfaces import IRequest
diff --git a/pyramid/tests/test_httpexceptions.py b/pyramid/tests/test_httpexceptions.py
new file mode 100644
index 000000000..843f9485a
--- /dev/null
+++ b/pyramid/tests/test_httpexceptions.py
@@ -0,0 +1,79 @@
+import unittest
+
+class Test_abort(unittest.TestCase):
+ def _callFUT(self, *arg, **kw):
+ from pyramid.httpexceptions import abort
+ return abort(*arg, **kw)
+
+ def test_status_404(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ self.assertRaises(HTTPNotFound().exception.__class__,
+ self._callFUT, 404)
+
+ def test_status_201(self):
+ from pyramid.httpexceptions import HTTPCreated
+ self.assertRaises(HTTPCreated().exception.__class__,
+ self._callFUT, 201)
+
+ def test_extra_kw(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ try:
+ self._callFUT(404, headers=[('abc', 'def')])
+ except HTTPNotFound().exception.__class__, exc:
+ self.assertEqual(exc.headers['abc'], 'def')
+ else: # pragma: no cover
+ raise AssertionError
+
+class Test_redirect(unittest.TestCase):
+ def _callFUT(self, *arg, **kw):
+ from pyramid.httpexceptions import redirect
+ return redirect(*arg, **kw)
+
+ def test_default(self):
+ from pyramid.httpexceptions import HTTPFound
+ try:
+ self._callFUT('http://example.com')
+ except HTTPFound().exception.__class__, exc:
+ self.assertEqual(exc.location, 'http://example.com')
+ self.assertEqual(exc.status, '302 Found')
+
+ def test_custom_code(self):
+ from pyramid.httpexceptions import HTTPMovedPermanently
+ try:
+ self._callFUT('http://example.com', 301)
+ except HTTPMovedPermanently().exception.__class__, exc:
+ self.assertEqual(exc.location, 'http://example.com')
+ self.assertEqual(exc.status, '301 Moved Permanently')
+
+ def test_extra_kw(self):
+ from pyramid.httpexceptions import HTTPFound
+ try:
+ self._callFUT('http://example.com', headers=[('abc', 'def')])
+ except HTTPFound().exception.__class__, exc:
+ self.assertEqual(exc.location, 'http://example.com')
+ self.assertEqual(exc.status, '302 Found')
+ self.assertEqual(exc.headers['abc'], 'def')
+
+
+class Test_default_httpexception_view(unittest.TestCase):
+ def _callFUT(self, context, request):
+ from pyramid.httpexceptions import default_httpexception_view
+ return default_httpexception_view(context, request)
+
+ def test_call_with_response(self):
+ from pyramid.response import Response
+ r = Response()
+ result = self._callFUT(r, None)
+ self.assertEqual(result, r)
+
+ def test_call_with_nonresponse(self):
+ request = DummyRequest()
+ result = self._callFUT(None, request)
+ self.assertEqual(result, 'response')
+
+class DummyRequest(object):
+ def get_response(self, context):
+ return 'response'
+
+
+