From def444c2c310c8cb117e0c5181bf74f5beed064c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 16 May 2009 20:59:41 +0000 Subject: Speed up common case (use default factory). --- CHANGES.txt | 4 +++ docs/narr/hooks.rst | 26 +++++++++++++++ repoze/bfg/interfaces.py | 4 +++ repoze/bfg/tests/test_urldispatch.py | 61 +++++++++++++++++++++++++++++++++++- repoze/bfg/urldispatch.py | 19 ++++++++--- 5 files changed, 108 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0a7610a7c..72c4a0fb6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -90,6 +90,10 @@ Features - The Routes ``Route`` object used to resolve the match is now put into the environment as ``bfg.route`` when URL dispatch is used. +- You can now change the default Routes "context factory" globally. + See the "ZCML Hooks" chapter of the documentation (in the "Changing + the Default Routes Context Factory" section). + 0.8a6 (2009-05-11) ================== diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 585e3e655..21906e466 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -167,3 +167,29 @@ factory: Forbidden`` status code, rather than use the default unauthorized application factory, which sends a response with a ``401 Unauthorized`` status code. + +Changing the Default Routes Context Factory +------------------------------------------- + +The default Routes "context factory" (the object used to create +context objects when you use ```` statements in your ZCML) is +``repoze.bfg.urldispatch.DefaultRoutesContext``. You may change the +class used as the Routes "context factory" by placing the following +ZCML in your ``configure.zcml`` file. + +.. code-block:: xml + :linenos: + + + +Replace ``helloworld.factories.routes_context_factory`` with the +Python dotted name to the context factory you want to use. Here's +some sample code that implements a minimal context factory: + +.. code-block:: python + + class RoutesContextFactory(object): + def __init__(self, **kw): + self.__dict__.update(kw) + diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py index dbbfd78a5..5dadd252a 100644 --- a/repoze/bfg/interfaces.py +++ b/repoze/bfg/interfaces.py @@ -215,6 +215,10 @@ class IContextURL(Interface): def __call__(): """ Return a URL that points to the context """ +class IRoutesContextFactory(Interface): + """ A marker interface used to look up the default routes context factory + """ + # VH_ROOT_KEY is an interface; its imported from other packages (e.g. # traversalwrapper) VH_ROOT_KEY = 'HTTP_X_VHM_ROOT' diff --git a/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py index e1640bfdf..e5ae483e7 100644 --- a/repoze/bfg/tests/test_urldispatch.py +++ b/repoze/bfg/tests/test_urldispatch.py @@ -1,6 +1,13 @@ import unittest +from repoze.bfg.testing import cleanUp class RoutesRootFactoryTests(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + def _getEnviron(self, **kw): environ = {'SERVER_NAME':'localhost', 'wsgi.url_scheme':'http'} @@ -15,6 +22,32 @@ class RoutesRootFactoryTests(unittest.TestCase): klass = self._getTargetClass() return klass(get_root) + def test_init_custom_default_context_factory_dont_decorate(self): + from zope.component import getGlobalSiteManager + from repoze.bfg.interfaces import IRoutesContextFactory + class Dummy(object): + pass + gsm = getGlobalSiteManager() + gsm.registerUtility(Dummy, IRoutesContextFactory) + mapper = self._makeOne(None) + self.assertEqual(mapper.default_context_factory, + Dummy) + self.assertEqual(mapper.decorate_context, True) + + def test_init_custom_default_context_factory_decorate(self): + from zope.component import getGlobalSiteManager + from repoze.bfg.interfaces import IRoutesContextFactory + from repoze.bfg.interfaces import IRoutesContext + from zope.interface import implements + class Dummy(object): + implements(IRoutesContext) + gsm = getGlobalSiteManager() + gsm.registerUtility(Dummy, IRoutesContextFactory) + mapper = self._makeOne(None) + self.assertEqual(mapper.default_context_factory, + Dummy) + self.assertEqual(mapper.decorate_context, False) + def test_no_route_matches(self): marker = () get_root = make_get_root(marker) @@ -80,12 +113,12 @@ class RoutesRootFactoryTests(unittest.TestCase): self.assertEqual(routing_args[la.encode('utf-8')], 'id') def test_no_fallback_get_root(self): + from repoze.bfg.urldispatch import RoutesContextNotFound marker = () mapper = self._makeOne(None) mapper.connect('wont', 'wont/:be/:found') environ = self._getEnviron(PATH_INFO='/archives/action1/article1') result = mapper(environ) - from repoze.bfg.urldispatch import RoutesContextNotFound self.failUnless(isinstance(result, RoutesContextNotFound)) def test_custom_factory(self): @@ -111,6 +144,32 @@ class RoutesRootFactoryTests(unittest.TestCase): self.failUnless(IDummy.providedBy(result)) self.failIf(hasattr(result, '_factory')) + def test_decorate_context_false(self): + from repoze.bfg.interfaces import IRoutesContext + class Dummy: + def __init__(self, **kw): + pass + mapper = self._makeOne(None) + mapper.connect('root', '') + environ = self._getEnviron(PATH_INFO='/') + mapper.decorate_context = False + mapper.default_context_factory = Dummy + result = mapper(environ) + self.failIf(IRoutesContext.providedBy(result)) + + def test_decorate_context_true(self): + from repoze.bfg.interfaces import IRoutesContext + class Dummy: + def __init__(self, **kw): + pass + mapper = self._makeOne(None) + mapper.connect('root', '') + environ = self._getEnviron(PATH_INFO='/') + mapper.decorate_context = True + mapper.default_context_factory = Dummy + result = mapper(environ) + self.failUnless(IRoutesContext.providedBy(result)) + def test_has_routes(self): mapper = self._makeOne(None) self.assertEqual(mapper.has_routes(), False) diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py index 2ad482c89..4c3bc2ed3 100644 --- a/repoze/bfg/urldispatch.py +++ b/repoze/bfg/urldispatch.py @@ -1,8 +1,11 @@ import re +from zope.component import queryUtility + +from zope.deprecation import deprecated + from zope.interface import implements from zope.interface import alsoProvides - from zope.interface import classProvides from routes import Mapper @@ -12,11 +15,10 @@ from routes import url_for from repoze.bfg.interfaces import IContextNotFound from repoze.bfg.interfaces import IContextURL from repoze.bfg.interfaces import IRoutesContext +from repoze.bfg.interfaces import IRoutesContextFactory from repoze.bfg.interfaces import ITraverser from repoze.bfg.interfaces import ITraverserFactory -from zope.deprecation import deprecated - _marker = () class DefaultRoutesContext(object): @@ -53,6 +55,7 @@ class RoutesRootFactory(Mapper): present as Routes match arguments dictionary keys. If no Routes route matches the current request, the 'fallback' get_root is called.""" + decorate_context = True def __init__(self, get_root=None, **kw): self.get_root = get_root kw['controller_scan'] = None @@ -61,6 +64,11 @@ class RoutesRootFactory(Mapper): kw['explicit'] = True Mapper.__init__(self, **kw) self._regs_created = False + context_factory = queryUtility(IRoutesContextFactory, + default=DefaultRoutesContext) + if IRoutesContext.implementedBy(context_factory): + self.decorate_context = False + self.default_context_factory = context_factory def has_routes(self): return bool(self.matchlist) @@ -103,8 +111,9 @@ class RoutesRootFactory(Mapper): kw[k] = v factory = route._factory if factory is None: - factory = DefaultRoutesContext - context = factory(**kw) + context = self.default_context_factory(**kw) + if self.decorate_context: + alsoProvides(context, IRoutesContext) else: context = factory(**kw) alsoProvides(context, IRoutesContext) -- cgit v1.2.3