From c8cf2256655ee4b34ec501325b9016608b5cce5f Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 15 Jan 2009 00:20:04 +0000 Subject: - Instead of invariably using ``webob.Request`` as the "request factory" (e.g. in the ``Router`` class) and ``webob.Response`` and the "response factory" (e.g. in ``render_template_to_response``), allow both to be overridden via a ZCML utility hook. See the "Using ZCML Hooks" chapter of the documentation for more information. --- CHANGES.txt | 12 ++++ docs/index.rst | 1 + docs/narr/hooks.rst | 48 +++++++++++++++ repoze/bfg/chameleon_genshi.py | 4 +- repoze/bfg/chameleon_text.py | 4 +- repoze/bfg/chameleon_zpt.py | 4 +- repoze/bfg/interfaces.py | 15 +++++ repoze/bfg/router.py | 9 ++- repoze/bfg/tests/test_chameleon_genshi.py | 59 +++++++------------ repoze/bfg/tests/test_chameleon_text.py | 43 +++++--------- repoze/bfg/tests/test_chameleon_zpt.py | 59 +++++++------------ repoze/bfg/tests/test_router.py | 98 ++++++++++++++++++------------- repoze/bfg/tests/test_view.py | 16 +++++ repoze/bfg/tests/test_wsgi.py | 29 +++++++++ repoze/bfg/tests/test_xslt.py | 22 +++++++ repoze/bfg/view.py | 4 +- repoze/bfg/wsgi.py | 7 ++- repoze/bfg/xslt.py | 4 +- 18 files changed, 288 insertions(+), 150 deletions(-) create mode 100644 docs/narr/hooks.rst diff --git a/CHANGES.txt b/CHANGES.txt index 9d17ca10e..2f5b14e84 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,15 @@ +After 0.6.2 +=========== + +Features +-------- + +- Instead of invariably using ``webob.Request`` as the "request + factory" (e.g. in the ``Router`` class) and ``webob.Response`` and + the "response factory" (e.g. in ``render_template_to_response``), + allow both to be overridden via a ZCML utility hook. See the "Using + ZCML Hooks" chapter of the documentation for more information. + 0.6.2 (2009-01-13) ================== diff --git a/docs/index.rst b/docs/index.rst index 13edb8a48..4df01341e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,6 +36,7 @@ Narrative documentation in chapter form explaining how to use narr/events narr/environment narr/unittesting + narr/hooks glossary API documentation diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst new file mode 100644 index 000000000..009943e9e --- /dev/null +++ b/docs/narr/hooks.rst @@ -0,0 +1,48 @@ +.. _hooks_chapter: + +Using ZCML Hooks +================ + +ZCML "hooks" can be used to influence the behavior of the +:mod:`repoze.bfg` framework in various ways. This is an advanced +topic; very few people will want or need to do any of these things. + +Changing the request factory +---------------------------- + +You may change the class used as the "request factory" from within the +:mod:`repoze.bfg` ``Router`` class (the ``Router`` class turns the +WSGI environment into a "request" object which is used ubiquitously +throughout BFG). The default "request factory" is the class +``webob.Request``. You may change it by placing the following ZCML in +your ``configure.zcml`` file. + +.. code-block:: xml + :linenos: + + + +Replace ``my.request.factory`` with the Python dotted name to the +request factory you want to use. + +Changing the response factory +----------------------------- + +You may change the class used as the "response factory" from within +the :mod:`repoze.bfg` ``chameleon_zpt``, ``chameleon_genshi``, +``chameleon_text`` (the ``render_template_to_response`` function used +within each) and other various places where a Response object is +constructed by :mod:`repoze.bfg`. The default "response factory" is +the class ``webob.Response``. You may change it by placing the +following ZCML in your ``configure.zcml`` file. + +.. code-block:: xml + :linenos: + + + +Replace ``my.response.factory`` with the Python dotted name to the +response factory you want to use. + diff --git a/repoze/bfg/chameleon_genshi.py b/repoze/bfg/chameleon_genshi.py index 3d8472203..8c33dee14 100644 --- a/repoze/bfg/chameleon_genshi.py +++ b/repoze/bfg/chameleon_genshi.py @@ -6,6 +6,7 @@ from zope.interface import implements from zope.interface import classProvides from zope.deprecation import deprecated +from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ISettings from repoze.bfg.interfaces import ITemplateRenderer from repoze.bfg.interfaces import ITemplateRendererFactory @@ -70,5 +71,6 @@ def render_template_to_response(path, **kw): renderer = renderer_from_cache(path, GenshiTemplateRenderer, auto_reload=auto_reload) result = renderer(**kw) - return Response(result) + response_factory = queryUtility(IResponseFactory, default=Response) + return response_factory(result) diff --git a/repoze/bfg/chameleon_text.py b/repoze/bfg/chameleon_text.py index e00106e5c..525957af1 100644 --- a/repoze/bfg/chameleon_text.py +++ b/repoze/bfg/chameleon_text.py @@ -5,6 +5,7 @@ from zope.component import queryUtility from zope.interface import classProvides from zope.interface import implements +from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ITemplateRenderer from repoze.bfg.interfaces import ITemplateRendererFactory from repoze.bfg.interfaces import ISettings @@ -81,5 +82,6 @@ def render_template_to_response(path, **kw): renderer = renderer_from_cache(path, TextTemplateRenderer, auto_reload=auto_reload) result = renderer(**kw) - return Response(result) + response_factory = queryUtility(IResponseFactory, default=Response) + return response_factory(result) diff --git a/repoze/bfg/chameleon_zpt.py b/repoze/bfg/chameleon_zpt.py index f7e9edadf..c3662770f 100644 --- a/repoze/bfg/chameleon_zpt.py +++ b/repoze/bfg/chameleon_zpt.py @@ -6,6 +6,7 @@ from zope.interface import classProvides from zope.interface import implements from zope.deprecation import deprecated +from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ITemplateRenderer from repoze.bfg.interfaces import ITemplateRendererFactory from repoze.bfg.interfaces import ISettings @@ -72,5 +73,6 @@ def render_template_to_response(path, **kw): renderer = renderer_from_cache(path, ZPTTemplateRenderer, auto_reload=auto_reload) result = renderer(**kw) - return Response(result) + response_factory = queryUtility(IResponseFactory, default=Response) + return response_factory(result) diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py index 92e906e51..013267b7f 100644 --- a/repoze/bfg/interfaces.py +++ b/repoze/bfg/interfaces.py @@ -28,6 +28,13 @@ deprecated( IRootPolicy = "repoze.bfg.interfaces:IRootFactory", ) +class IRequestFactory(Interface): + """ A utility which generates a request factory """ + def __call__(self): + """ Return a request factory (e.g. a callable that accepts an + environ and returns an object implementing IRequest, + e.g. ``webob.Request``)""" + class IRequest(Interface): """ Request type interface attached to all request objects """ @@ -46,6 +53,14 @@ class IDELETERequest(IRequest): class IHEADRequest(IRequest): """ Request type interface attached to HEAD requests""" +class IResponseFactory(Interface): + """ A utility which generates a response factory """ + def __call__(self): + """ Return a response factory (e.g. a callable that returns an + object implementing IResponse, e.g. ``webob.Response``; it + should accept all the arguments that the webob.Response class + accepts)""" + class IResponse(Interface): status = Attribute('WSGI status code of response') headerlist = Attribute('List of response headers') diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py index 8e1b998d8..755434870 100644 --- a/repoze/bfg/router.py +++ b/repoze/bfg/router.py @@ -21,6 +21,7 @@ from repoze.bfg.events import WSGIApplicationCreatedEvent from repoze.bfg.interfaces import ILogger from repoze.bfg.interfaces import ITraverserFactory from repoze.bfg.interfaces import IRequest +from repoze.bfg.interfaces import IRequestFactory from repoze.bfg.interfaces import HTTP_METHOD_INTERFACES from repoze.bfg.interfaces import IRouter @@ -45,6 +46,11 @@ class Router(object): def __init__(self, registry): self.registry = registry + @property + def root_policy(self): + """ Backwards compatibility alias """ + return self.registry.getUtility(IRootFactory) + def __call__(self, environ, start_response): """ Accept ``environ`` and ``start_response``; route requests to @@ -54,7 +60,8 @@ class Router(object): registry_manager.push(self.registry) try: - request = Request(environ) + request_factory = queryUtility(IRequestFactory, default=Request) + request = request_factory(environ) directlyProvides(request, IRequest) also = HTTP_METHOD_INTERFACES.get(request.method) diff --git a/repoze/bfg/tests/test_chameleon_genshi.py b/repoze/bfg/tests/test_chameleon_genshi.py index 3b0c05b34..9b210e1ad 100644 --- a/repoze/bfg/tests/test_chameleon_genshi.py +++ b/repoze/bfg/tests/test_chameleon_genshi.py @@ -1,13 +1,13 @@ import unittest -from zope.component.testing import PlacelessSetup +from zope.testing.cleanup import cleanUp -class Base(PlacelessSetup): +class Base(object): def setUp(self): - PlacelessSetup.setUp(self) + cleanUp() def tearDown(self): - PlacelessSetup.tearDown(self) + cleanUp() def _zcmlConfigure(self): import repoze.bfg.includes @@ -20,13 +20,7 @@ class Base(PlacelessSetup): here = os.path.abspath(os.path.dirname(__file__)) return os.path.join(here, 'fixtures', name) -class GenshiTemplateRendererTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class GenshiTemplateRendererTests(Base, unittest.TestCase): def _getTargetClass(self): from repoze.bfg.chameleon_genshi import GenshiTemplateRenderer return GenshiTemplateRenderer @@ -64,13 +58,7 @@ class GenshiTemplateRendererTests(unittest.TestCase, Base): self.assertEqual(result, '
\n
') -class RenderTemplateTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class RenderTemplateTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_genshi import render_template return render_template @@ -83,13 +71,7 @@ class RenderTemplateTests(unittest.TestCase, Base): self.assertEqual(result, '
\n
') -class RenderTemplateToResponseTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class RenderTemplateToResponseTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_genshi import render_template_to_response return render_template_to_response @@ -105,13 +87,20 @@ class RenderTemplateToResponseTests(unittest.TestCase, Base): self.assertEqual(result.status, '200 OK') self.assertEqual(len(result.headerlist), 2) -class GetRendererTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) + def test_iresponsefactory_override(self): + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + from webob import Response + class Response2(Response): + pass + from repoze.bfg.interfaces import IResponseFactory + gsm.registerUtility(Response2, IResponseFactory) + minimal = self._getTemplatePath('minimal.genshi') + render = self._getFUT() + result = render(minimal) + self.failUnless(isinstance(result, Response2)) +class GetRendererTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_genshi import get_renderer return get_renderer @@ -160,13 +149,7 @@ class GetRendererTests(unittest.TestCase, Base): self.failUnless(result is utility) -class GetTemplateTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class GetTemplateTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_genshi import get_template return get_template diff --git a/repoze/bfg/tests/test_chameleon_text.py b/repoze/bfg/tests/test_chameleon_text.py index f61cfe963..992957d0e 100644 --- a/repoze/bfg/tests/test_chameleon_text.py +++ b/repoze/bfg/tests/test_chameleon_text.py @@ -20,13 +20,7 @@ class Base: here = os.path.abspath(os.path.dirname(__file__)) return os.path.join(here, 'fixtures', name) -class TextTemplateRendererTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class TextTemplateRendererTests(Base, unittest.TestCase): def _getTargetClass(self): from repoze.bfg.chameleon_text import TextTemplateRenderer return TextTemplateRenderer @@ -70,13 +64,7 @@ class TextTemplateRendererTests(unittest.TestCase, Base): self.failUnless(isinstance(result, str)) self.assertEqual(result, 'Hello.\n') -class RenderTemplateTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class RenderTemplateTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_text import render_template return render_template @@ -89,13 +77,7 @@ class RenderTemplateTests(unittest.TestCase, Base): self.failUnless(isinstance(result, str)) self.assertEqual(result, 'Hello.\n') -class RenderTemplateToResponseTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class RenderTemplateToResponseTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_text import render_template_to_response return render_template_to_response @@ -111,13 +93,20 @@ class RenderTemplateToResponseTests(unittest.TestCase, Base): self.assertEqual(result.status, '200 OK') self.assertEqual(len(result.headerlist), 2) -class GetRendererTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) + def test_iresponsefactory_override(self): + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + from webob import Response + class Response2(Response): + pass + from repoze.bfg.interfaces import IResponseFactory + gsm.registerUtility(Response2, IResponseFactory) + minimal = self._getTemplatePath('minimal.txt') + render = self._getFUT() + result = render(minimal) + self.failUnless(isinstance(result, Response2)) +class GetRendererTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_text import get_renderer return get_renderer diff --git a/repoze/bfg/tests/test_chameleon_zpt.py b/repoze/bfg/tests/test_chameleon_zpt.py index 995dd92e1..088930069 100644 --- a/repoze/bfg/tests/test_chameleon_zpt.py +++ b/repoze/bfg/tests/test_chameleon_zpt.py @@ -1,13 +1,13 @@ import unittest -from zope.component.testing import PlacelessSetup +from zope.testing.cleanup import cleanUp -class Base(PlacelessSetup): +class Base(object): def setUp(self): - PlacelessSetup.setUp(self) + cleanUp() def tearDown(self): - PlacelessSetup.tearDown(self) + cleanUp() def _zcmlConfigure(self): import repoze.bfg.includes @@ -20,13 +20,7 @@ class Base(PlacelessSetup): here = os.path.abspath(os.path.dirname(__file__)) return os.path.join(here, 'fixtures', name) -class ZPTTemplateRendererTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class ZPTTemplateRendererTests(Base, unittest.TestCase): def _getTargetClass(self): from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer return ZPTTemplateRenderer @@ -65,13 +59,7 @@ class ZPTTemplateRendererTests(unittest.TestCase, Base): '
\n
') -class RenderTemplateTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class RenderTemplateTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_zpt import render_template return render_template @@ -85,13 +73,7 @@ class RenderTemplateTests(unittest.TestCase, Base): self.assertEqual(result, '
\n
') -class RenderTemplateToResponseTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class RenderTemplateToResponseTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_zpt import render_template_to_response return render_template_to_response @@ -108,13 +90,20 @@ class RenderTemplateToResponseTests(unittest.TestCase, Base): self.assertEqual(result.status, '200 OK') self.assertEqual(len(result.headerlist), 2) -class GetRendererTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) + def test_iresponsefactory_override(self): + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + from webob import Response + class Response2(Response): + pass + from repoze.bfg.interfaces import IResponseFactory + gsm.registerUtility(Response2, IResponseFactory) + minimal = self._getTemplatePath('minimal.pt') + render = self._getFUT() + result = render(minimal) + self.failUnless(isinstance(result, Response2)) +class GetRendererTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_zpt import get_renderer return get_renderer @@ -162,13 +151,7 @@ class GetRendererTests(unittest.TestCase, Base): result = get('foo') self.failUnless(result is utility) -class GetTemplateTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - - def tearDown(self): - Base.tearDown(self) - +class GetTemplateTests(Base, unittest.TestCase): def _getFUT(self): from repoze.bfg.chameleon_zpt import get_template return get_template diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py index 298065d9b..e0d27b332 100644 --- a/repoze/bfg/tests/test_router.py +++ b/repoze/bfg/tests/test_router.py @@ -1,17 +1,15 @@ import unittest -from zope.component.testing import PlacelessSetup +from zope.testing.cleanup import cleanUp -class RouterTests(unittest.TestCase, PlacelessSetup): +class RouterTests(unittest.TestCase): def setUp(self): - PlacelessSetup.setUp(self) + cleanUp() def tearDown(self): - PlacelessSetup.tearDown(self) + cleanUp() def _registerLogger(self): - import zope.component - gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import ILogger class Logger: def __init__(self): @@ -20,12 +18,12 @@ class RouterTests(unittest.TestCase, PlacelessSetup): self.messages.append(msg) debug = info logger = Logger() + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() gsm.registerUtility(logger, ILogger, name='repoze.bfg.debug') return logger def _registerSettings(self, **kw): - import zope.component - gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import ISettings class Settings: def __init__(self, **kw): @@ -34,41 +32,47 @@ class RouterTests(unittest.TestCase, PlacelessSetup): defaultkw = {'debug_authorization':False, 'debug_notfound':False} defaultkw.update(kw) settings = Settings(**defaultkw) + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() gsm.registerUtility(settings, ISettings) def _registerTraverserFactory(self, app, name, *for_): - import zope.component - gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import ITraverserFactory + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() gsm.registerAdapter(app, for_, ITraverserFactory, name) def _registerView(self, app, name, *for_): - import zope.component - gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import IView + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() gsm.registerAdapter(app, for_, IView, name) def _registerPermission(self, permission, name, *for_): - import zope.component - gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import IViewPermission + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() gsm.registerAdapter(permission, for_, IViewPermission, name) def _registerSecurityPolicy(self, secpol): - import zope.component - gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import ISecurityPolicy + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() gsm.registerUtility(secpol, ISecurityPolicy) - def _registerEventListener(self, listener, iface): - import zope.component - gsm = zope.component.getGlobalSiteManager() + def _registerEventListener(self, iface): + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + L = [] + def listener(event): + L.append(event) gsm.registerHandler(listener, (iface,)) + return L def _registerRootFactory(self, root_factory): - import zope.component - gsm = zope.component.getGlobalSiteManager() from repoze.bfg.interfaces import IRootFactory + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() gsm.registerUtility(root_factory, IRootFactory) def _getTargetClass(self): @@ -424,14 +428,8 @@ class RouterTests(unittest.TestCase, PlacelessSetup): self._registerView(view, '', None, None) from repoze.bfg.interfaces import INewRequest from repoze.bfg.interfaces import INewResponse - request_events = [] - response_events = [] - def handle_request(event): - request_events.append(event) - def handle_response(event): - response_events.append(event) - self._registerEventListener(handle_request, INewRequest) - self._registerEventListener(handle_response, INewResponse) + request_events = self._registerEventListener(INewRequest) + response_events = self._registerEventListener(INewResponse) self._registerRootFactory(rootfactory) router = self._makeOne(None) start_response = DummyStartResponse() @@ -458,10 +456,7 @@ class RouterTests(unittest.TestCase, PlacelessSetup): self._registerRootFactory(rootfactory) router = self._makeOne(None) start_response = DummyStartResponse() - request_events = [] - def handle_request(event): - request_events.append(event) - self._registerEventListener(handle_request, INewRequest) + request_events = self._registerEventListener(INewRequest) result = router(environ, start_response) request = request_events[0].request self.failUnless(IPOSTRequest.providedBy(request)) @@ -485,22 +480,45 @@ class RouterTests(unittest.TestCase, PlacelessSetup): self._registerRootFactory(rootfactory) router = self._makeOne(None) start_response = DummyStartResponse() - request_events = [] - def handle_request(event): - request_events.append(event) - self._registerEventListener(handle_request, INewRequest) + request_events = self._registerEventListener(INewRequest) result = router(environ, start_response) request = request_events[0].request self.failUnless(IPUTRequest.providedBy(request)) self.failIf(IPOSTRequest.providedBy(request)) self.failUnless(IRequest.providedBy(request)) + + def test_call_irequestfactory_override(self): + from repoze.bfg.interfaces import INewRequest + from repoze.bfg.interfaces import IRequestFactory + from webob import Request + class Request2(Request): + pass + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + gsm.registerUtility(Request2, IRequestFactory) + rootfactory = make_rootfactory(None) + context = DummyContext() + traversalfactory = make_traversal_factory(context, '', []) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = make_view(response) + environ = self._makeEnviron() + self._registerTraverserFactory(traversalfactory, '', None) + self._registerView(view, '', None, None) + self._registerRootFactory(rootfactory) + router = self._makeOne(None) + start_response = DummyStartResponse() + request_events = self._registerEventListener(INewRequest) + result = router(environ, start_response) + request = request_events[0].request + self.failUnless(isinstance(request, Request2)) -class MakeAppTests(unittest.TestCase, PlacelessSetup): +class MakeAppTests(unittest.TestCase): def setUp(self): - PlacelessSetup.setUp(self) + cleanUp() def tearDown(self): - PlacelessSetup.tearDown(self) + cleanUp() def _callFUT(self, *arg, **kw): from repoze.bfg.router import make_app diff --git a/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py index 389c00cc5..6f16da28e 100644 --- a/repoze/bfg/tests/test_view.py +++ b/repoze/bfg/tests/test_view.py @@ -468,6 +468,22 @@ class TestStaticView(unittest.TestCase, BaseTest): filedata = open(os.path.join(static_dir, '__init__.py')).read() self.assertEqual(result, filedata) + def test_it_with_alternate_iresponsefactory(self): + view = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.subpath = ['__init__.py'] + request.environ = self._makeEnviron() + from repoze.bfg.interfaces import IResponseFactory + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + from webob import Response + class Response2(Response): + pass + gsm.registerUtility(Response2, IResponseFactory) + response = view(context, request) + self.failUnless(isinstance(response, Response2)) + class DummyContext: pass diff --git a/repoze/bfg/tests/test_wsgi.py b/repoze/bfg/tests/test_wsgi.py index 4c36850b6..ac02ec49f 100644 --- a/repoze/bfg/tests/test_wsgi.py +++ b/repoze/bfg/tests/test_wsgi.py @@ -1,6 +1,13 @@ import unittest +from zope.testing.cleanup import cleanUp class WSGIAppTests(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + def test_decorator(self): body = 'Unauthorized' headerlist = [ ('Content-Type', 'text/plain'), @@ -18,6 +25,28 @@ class WSGIAppTests(unittest.TestCase): self.assertEqual(response.headerlist, headerlist) self.assertEqual(response.app_iter, [body]) + def test_decorator_alternate_iresponsefactory(self): + body = 'Unauthorized' + headerlist = [ ('Content-Type', 'text/plain'), + ('Content-Length', len(body)) ] + status = '401 Unauthorized' + def real_wsgiapp(environ, start_response): + start_response(status, headerlist) + return [body] + from repoze.bfg.wsgi import wsgiapp + wrapped = wsgiapp(real_wsgiapp) + context = DummyContext() + request = DummyRequest({}) + from repoze.bfg.interfaces import IResponseFactory + from zope.component import getGlobalSiteManager + from webob import Response + class Response2(Response): + pass + gsm = getGlobalSiteManager() + gsm.registerUtility(Response2, IResponseFactory) + response = wrapped(context, request) + self.failUnless(isinstance(response, Response2)) + def test_decorator_startresponse_uncalled(self): body = 'Unauthorized' headerlist = [ ('Content-Type', 'text/plain'), diff --git a/repoze/bfg/tests/test_xslt.py b/repoze/bfg/tests/test_xslt.py index 3755ad8a5..38e693937 100644 --- a/repoze/bfg/tests/test_xslt.py +++ b/repoze/bfg/tests/test_xslt.py @@ -137,6 +137,28 @@ class RenderTransformToResponseTests(Base, unittest.TestCase): self.assertEqual(len(result.headerlist), 2) self.assertEqual(queryUtility(INodeTemplateRenderer, minimal), utility) + def test_alternate_iresponse_factory(self): + self._zcmlConfigure() + from repoze.bfg.interfaces import IResponseFactory + from zope.component import getGlobalSiteManager + gsm = getGlobalSiteManager() + from webob import Response + class Response2(Response): + pass + gsm.registerUtility(Response2, IResponseFactory) + from zope.component import getGlobalSiteManager + from repoze.bfg.xslt import XSLTemplateRenderer + from repoze.bfg.interfaces import INodeTemplateRenderer + minimal = self._getTemplatePath('minimal.xsl') + utility = XSLTemplateRenderer(minimal) + gsm = getGlobalSiteManager() + gsm.registerUtility(utility, INodeTemplateRenderer, name=minimal) + from lxml import etree + info = etree.Element("info") + result = self._callFUT(minimal, node=info) + self.failUnless(isinstance(result, Response2)) + + class RenderTransformTests(Base, unittest.TestCase): def _callFUT(self, path, node): from repoze.bfg.xslt import render_transform diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py index 39829af62..3f9216b0d 100644 --- a/repoze/bfg/view.py +++ b/repoze/bfg/view.py @@ -4,6 +4,7 @@ from webob import Response from zope.component import queryMultiAdapter from zope.component import queryUtility +from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ISecurityPolicy from repoze.bfg.interfaces import IViewPermission from repoze.bfg.interfaces import IView @@ -154,7 +155,8 @@ class static(object): ecopy['SCRIPT_NAME'] = '' body = self.app(ecopy, catch_start_response) status, headers, exc_info = caught - response = Response() + response_factory = queryUtility(IResponseFactory, default=Response) + response = response_factory() response.app_iter = body response.status = status response.headerlist = headers diff --git a/repoze/bfg/wsgi.py b/repoze/bfg/wsgi.py index 149273c27..93b2c143b 100644 --- a/repoze/bfg/wsgi.py +++ b/repoze/bfg/wsgi.py @@ -1,3 +1,7 @@ +from zope.component import queryUtility + +from repoze.bfg.interfaces import IResponseFactory + from webob import Response try: from functools import wraps @@ -37,7 +41,8 @@ def wsgiapp(wrapped): body = wrapped(environ, catch_start_response) if caught: status, headers, exc_info = caught - response = Response() + response_factory = queryUtility(IResponseFactory, default=Response) + response = response_factory() response.app_iter = body response.status = status response.headerlist = headers diff --git a/repoze/bfg/xslt.py b/repoze/bfg/xslt.py index 8c03dc493..7dae7ca8e 100644 --- a/repoze/bfg/xslt.py +++ b/repoze/bfg/xslt.py @@ -12,6 +12,7 @@ from zope.interface import implements from repoze.bfg.path import caller_path from repoze.bfg.interfaces import INodeTemplateRenderer +from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import ITemplateRendererFactory def get_transform(path, node): @@ -46,7 +47,8 @@ def render_transform_to_response(path, node, **kw): the lxml node at ``node`` and return a Response object.""" path = caller_path(path) result = render_transform(path, node, **kw) - return Response(result) + response_factory = queryUtility(IResponseFactory, default=Response) + return response_factory(result) class XSLTemplateRenderer(object): classProvides(ITemplateRendererFactory) -- cgit v1.2.3