summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-05-31 03:13:13 +0000
committerChris McDonough <chrism@agendaless.com>2009-05-31 03:13:13 +0000
commit4936f862588240c51e619a65aa5d574ba29d784b (patch)
treef73f81fa6f2603773948598218d32d74c6475ee2
parent964b7852d997f6c4aa4b04d54f2847095e4461e8 (diff)
downloadpyramid-4936f862588240c51e619a65aa5d574ba29d784b.tar.gz
pyramid-4936f862588240c51e619a65aa5d574ba29d784b.tar.bz2
pyramid-4936f862588240c51e619a65aa5d574ba29d784b.zip
Features
-------- - It is now possible to register a custom ``repoze.bfg.interfaces.INotFoundView`` for a given application. This feature replaces the ``repoze.bfg.interfaces.INotFoundAppFactory`` feature previously described in the Hooks chapter. The INotFoundView will be called when the framework detects that a view lookup done as a result of a reqest fails; it should accept a context object and a request object; it should return an IResponse object (a webob response, basically). See the Hooks narrative chapter of the BFG docs for more info. Deprecations ------------ - The ``repoze.bfg.interfaces.IUnauthorizedAppFactory`` interface has been deprecated in favor of using the new ``repoze.bfg.interfaces.IForbiddenResponseFactory`` mechanism.
-rw-r--r--CHANGES.txt25
-rw-r--r--docs/narr/hooks.rst42
-rw-r--r--repoze/bfg/interfaces.py19
-rw-r--r--repoze/bfg/router.py50
-rw-r--r--repoze/bfg/tests/test_router.py30
5 files changed, 112 insertions, 54 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 1547eeb97..04ea3e0aa 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,9 +1,34 @@
Next release
============
+Features
+--------
+
+- It is now possible to register a custom
+ ``repoze.bfg.interfaces.INotFoundView`` for a given application.
+ This feature replaces the
+ ``repoze.bfg.interfaces.INotFoundAppFactory`` feature previously
+ described in the Hooks chapter. The INotFoundView will be called
+ when the framework detects that a view lookup done as a result of a
+ reqest fails; it should accept a context object and a request
+ object; it should return an IResponse object (a webob response,
+ basically). See the Hooks narrative chapter of the BFG docs for
+ more info.
+
+Deprecations
+------------
+
+- The ``repoze.bfg.interfaces.IUnauthorizedAppFactory`` interface has
+ been deprecated in favor of using the new
+ ``repoze.bfg.interfaces.IForbiddenResponseFactory`` mechanism.
+
+Renames
+-------
+
- Renamed ``repoze.bfg.interfaces.IForbiddenResponseFactory`` to
``repoze.bfg.interfaces.IForbiddenView``.
+
0.9a7 (2009-05-30)
==================
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index bc00b28a7..ace2c847b 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -88,41 +88,37 @@ an object that implements any particular interface; it simply needs
have a ``status`` attribute, a ``headerlist`` attribute, and and
``app_iter`` attribute.
-Changing the NotFound Application
----------------------------------
+Changing the Not Found View
+---------------------------
-When :mod:`repoze.bfg` can't map a URL to code, it creates and invokes
-a NotFound WSGI application. The application it invokes can be
-customized by placing something like the following ZCML in your
-``configure.zcml`` file.
+When :mod:`repoze.bfg` can't map a URL to view code, it invokes a
+notfound :term:`view`. The view it invokes can be customized by
+placing something like the following ZCML in your ``configure.zcml``
+file.
.. code-block:: xml
:linenos:
- <utility provides="repoze.bfg.interfaces.INotFoundAppFactory"
- component="helloworld.factories.notfound_app_factory"/>
+ <utility provides="repoze.bfg.interfaces.INotFoundView"
+ component="helloworld.views.notfound_view"/>
-Replace ``helloworld.factories.notfound_app_factory`` with the Python
-dotted name to the request factory you want to use. Here's some
-sample code that implements a minimal NotFound application factory:
+Replace ``helloworld.views.notfound_view`` with the Python dotted name
+to the notfound view you want to use. Here's some sample code that
+implements a minimal NotFound view:
.. code-block:: python
from webob.exc import HTTPNotFound
- class MyNotFound(HTTPNotFound):
- pass
+ def notfound_view(context, request):
+ return HTTPNotFound()
- def notfound_app_factory():
- return MyNotFound
-
-.. note:: When a NotFound application factory is invoked, it is passed
- the WSGI environ and the WSGI ``start_response`` handler by
- :mod:`repoze.bfg`. Within the WSGI environ will be a key named
- ``repoze.bfg.message`` that has a value explaining why the not
- found error was raised. This error will be different when the
- ``debug_notfound`` environment setting is true than it is when it
- is false.
+.. note:: When a NotFound view is invoked, it is passed a request.
+ The ``environ`` attribute of the request is the WSGI environment.
+ Within the WSGI environ will be a key named ``repoze.bfg.message``
+ that has a value explaining why the not found error was raised.
+ This error will be different when the ``debug_notfound``
+ environment setting is true than it is when it is false.
Changing the Forbidden View
---------------------------
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index 8316c58bc..79ef25f09 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -184,18 +184,21 @@ class IForbiddenView(Interface):
repoze.bfg router during traversal or url dispatch. The
``request`` will be the request object which caused the deny."""
-class INotFoundAppFactory(Interface):
- """ A utility which returns a NotFound WSGI application factory """
- def __call__():
- """ Return a callable which returns a notfound WSGI
- application. When the WSGI application is invoked,
+class INotFoundView(Interface):
+ """ A utility which returns a NotFound response (an IResponse)
+ when a view cannot be located for a particular URL"""
+ def __call__(context, request):
+ """ Return a NotFound response. When the view is rendered,
a``message`` key in the WSGI environ provides information
- pertaining to the reason for the notfound."""
+ pertaining to the reason for the notfound error."""
+
+class INotFoundAppFactory(Interface):
+ """ A utility which returns a NotFound WSGI application factory.
+ Deprecated in repoze.bfg 0.9 in favor of INotFoundView"""
class IUnauthorizedAppFactory(Interface):
""" A utility which returns an Unauthorized WSGI application
- factory (deprecated in repoze.bfg 0.8.2) in favor of
- IForbiddenResponseFactory """
+ factory. Deprecated in repoze.bfg 0.9 in favor of IForbiddenView"""
class IContextURL(Interface):
""" An adapter which deals with URLs related to a context.
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index 6e6477be1..32b8c5b72 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -50,8 +50,6 @@ from repoze.bfg.traversal import _traverse
from repoze.bfg.urldispatch import RoutesRootFactory
-from repoze.bfg.wsgi import NotFound
-
_marker = object()
class Router(object):
@@ -68,17 +66,17 @@ class Router(object):
self.request_factory = registry.queryUtility(IRequestFactory)
+ forbidden = None
+
unauthorized_app_factory = registry.queryUtility(
IUnauthorizedAppFactory)
- forbidden = None
-
if unauthorized_app_factory is not None:
warning = (
'Instead of registering a utility against the '
'repoze.bfg.interfaces.IUnauthorizedAppFactory interface '
'to return a custom forbidden response, you should now '
- 'register a "repoze.interfaces.IForbiddenView". '
+ 'register a repoze.interfaces.IForbiddenView.'
'The IUnauthorizedAppFactory interface was deprecated in '
'repoze.bfg 0.9 and will be removed in a subsequent version '
'of repoze.bfg. See the "Hooks" chapter of the repoze.bfg '
@@ -92,10 +90,30 @@ class Router(object):
forbidden = registry.queryUtility(IForbiddenView, default=forbidden)
- self.forbidden_resp_factory = forbidden or default_forbidden_view
+ self.forbidden_view = forbidden or default_forbidden_view
+
+ notfound = None
- self.notfound_app_factory = registry.queryUtility(INotFoundAppFactory,
- default=NotFound)
+ notfound_app_factory = registry.queryUtility(INotFoundAppFactory)
+
+ if notfound_app_factory is not None:
+ warning = (
+ 'Instead of registering a utility against the '
+ 'repoze.bfg.interfaces.INotFoundAppFactory interface '
+ 'to return a custom notfound response, you should register '
+ 'a repoze.bfg.interfaces.INotFoundView. The '
+ 'INotFoundAppFactory interface was deprecated in'
+ 'repoze.bfg 0.9 and will be removed in a subsequent version '
+ 'of repoze.bfg. See the "Hooks" chapter of the repoze.bfg '
+ 'documentation for more information about '
+ 'INotFoundView.')
+ self.logger and self.logger.warn(warning)
+ def notfound(context, request):
+ app = notfound_app_factory()
+ response = request.get_response(app)
+ return response
+
+ self.notfound_view = notfound or default_notfound_view
settings = registry.queryUtility(ISettings)
if settings is not None:
@@ -203,7 +221,7 @@ class Router(object):
environ['repoze.bfg.message'] = msg
- response = self.forbidden_resp_factory(context, request)
+ response = self.forbidden_view(context, request)
start_response(response.status, response.headerlist)
return response.app_iter
@@ -223,8 +241,9 @@ class Router(object):
else:
msg = request.url
environ['repoze.bfg.message'] = msg
- notfound_app = self.notfound_app_factory()
- return notfound_app(environ, start_response)
+ response = self.notfound_view(context, request)
+ start_response(response.status, response.headerlist)
+ return response.app_iter
registry.has_listeners and registry.notify(NewResponse(response))
@@ -238,8 +257,7 @@ class Router(object):
finally:
self.threadlocal_manager.pop()
-def default_forbidden_view(context, request):
- status = '401 Unauthorized'
+def default_view(context, request, status):
try:
msg = escape(request.environ['repoze.bfg.message'])
except KeyError:
@@ -260,6 +278,12 @@ def default_forbidden_view(context, request):
headerlist = headers,
app_iter = [html])
+def default_forbidden_view(context, request):
+ return default_view(context, request, '401 Unauthorized')
+
+def default_notfound_view(context, request):
+ return default_view(context, request, '404 Not Found')
+
def make_app(root_factory, package=None, filename='configure.zcml',
authentication_policy=None, authorization_policy=None,
options=None, registry=None, debug_logger=None):
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index 86d7d12cf..3009e65dd 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -127,14 +127,24 @@ class RouterTests(unittest.TestCase):
router = self._makeOne()
self.assertEqual(router.root_policy, rootfactory)
- def test_inotfound_appfactory_override(self):
+ def test_secpol_with_inotfound_appfactory_BBB(self):
from repoze.bfg.interfaces import INotFoundAppFactory
- def app():
- """ """
- self.registry.registerUtility(app, INotFoundAppFactory)
- self._registerRootFactory(None)
+ environ = self._makeEnviron()
+ context = DummyContext()
+ self._registerTraverserFactory(context)
+ rootfactory = self._registerRootFactory(None)
+ logger = self._registerLogger()
+ def factory():
+ return 'yo'
+ self.registry.registerUtility(factory, INotFoundAppFactory)
router = self._makeOne()
- self.assertEqual(router.notfound_app_factory, app)
+ self.assertEqual(len(logger.messages), 1)
+ self.failUnless('INotFoundView' in logger.messages[0])
+ class DummyRequest:
+ def get_response(self, app):
+ return app
+ req = DummyRequest()
+ self.assertEqual(router.notfound_view(None, req), 'yo')
def test_iforbidden_responsefactory_override(self):
from repoze.bfg.interfaces import IForbiddenView
@@ -143,16 +153,16 @@ class RouterTests(unittest.TestCase):
self.registry.registerUtility(app, IForbiddenView)
self._registerRootFactory(None)
router = self._makeOne()
- self.assertEqual(router.forbidden_resp_factory, app)
+ self.assertEqual(router.forbidden_view, app)
def test_iforbidden_responsefactory_nooverride(self):
context = DummyContext()
self._registerRootFactory(None)
router = self._makeOne()
from repoze.bfg.router import default_forbidden_view
- self.assertEqual(router.forbidden_resp_factory, default_forbidden_view)
+ self.assertEqual(router.forbidden_view, default_forbidden_view)
- def test_secpol_with_iunauthorized_appfactory(self):
+ def test_secpol_with_iunauthorized_appfactory_BBB(self):
from repoze.bfg.interfaces import IUnauthorizedAppFactory
environ = self._makeEnviron()
context = DummyContext()
@@ -169,7 +179,7 @@ class RouterTests(unittest.TestCase):
def get_response(self, app):
return app
req = DummyRequest()
- self.assertEqual(router.forbidden_resp_factory(None, req), 'yo')
+ self.assertEqual(router.forbidden_view(None, req), 'yo')
def test_call_no_view_registered_no_isettings(self):
environ = self._makeEnviron()