summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-05-18 07:07:12 +0000
committerChris McDonough <chrism@agendaless.com>2009-05-18 07:07:12 +0000
commit916f88578ad68470a35a4b7afd223e9dbf5fd20d (patch)
tree52913d78e5876ca2612da56c6d066227001d9160
parent8e2f6eaae104df8bf13678a67f4690294f982e2d (diff)
downloadpyramid-916f88578ad68470a35a4b7afd223e9dbf5fd20d.tar.gz
pyramid-916f88578ad68470a35a4b7afd223e9dbf5fd20d.tar.bz2
pyramid-916f88578ad68470a35a4b7afd223e9dbf5fd20d.zip
Features
-------- - Added a ``traverse`` function to the ``repoze.bfg.traversal`` module. This function may be used to retrieve certain values computed during path resolution. See the Traversal API chapter of the documentation for more information about this function. Deprecations ------------ - Internal: ``ITraverser`` callables should now return a dictionary rather than a tuple. Up until 0.7.0, all ITraversers were assumed to return a 3-tuple. In 0.7.1, ITraversers were assumed to return a 6-tuple. As (by evidence) it's likely we'll need to add further information to the return value of an ITraverser callable, 0.8 assumes that an ITraverser return a dictionary with certain elements in it. See the ``repoze.bfg.interfaces.ITraverser`` interface for the list of keys that should be present in the dictionary. ``ITraversers`` which return tuples will still work, although a deprecation warning will be issued. Backwards Incompatibilities --------------------------- - If your code used the ITraverser interface directly (not via an API function such as ``find_model``) via an adapter lookup, you'll need to change your code to expect a dictionary rather than a 3- or 6-tuple if your code ever gets return values from the default ModelGraphTraverser or RoutesModelTraverser adapters.
-rw-r--r--CHANGES.txt31
-rw-r--r--docs/api/traversal.rst2
-rw-r--r--repoze/bfg/interfaces.py29
-rw-r--r--repoze/bfg/router.py42
-rw-r--r--repoze/bfg/testing.py5
-rw-r--r--repoze/bfg/tests/test_router.py168
-rw-r--r--repoze/bfg/tests/test_testing.py16
-rw-r--r--repoze/bfg/tests/test_traversal.py282
-rw-r--r--repoze/bfg/tests/test_urldispatch.py38
-rw-r--r--repoze/bfg/traversal.py213
-rw-r--r--repoze/bfg/urldispatch.py4
11 files changed, 563 insertions, 267 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 3e4dac521..fd4a82cb2 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,7 +1,36 @@
0.8dev (unreleased)
===================
-...
+Features
+--------
+
+- Added a ``traverse`` function to the ``repoze.bfg.traversal``
+ module. This function may be used to retrieve certain values
+ computed during path resolution. See the Traversal API chapter of
+ the documentation for more information about this function.
+
+Deprecations
+------------
+
+- Internal: ``ITraverser`` callables should now return a dictionary
+ rather than a tuple. Up until 0.7.0, all ITraversers were assumed
+ to return a 3-tuple. In 0.7.1, ITraversers were assumed to return a
+ 6-tuple. As (by evidence) it's likely we'll need to add further
+ information to the return value of an ITraverser callable, 0.8
+ assumes that an ITraverser return a dictionary with certain elements
+ in it. See the ``repoze.bfg.interfaces.ITraverser`` interface for
+ the list of keys that should be present in the dictionary.
+ ``ITraversers`` which return tuples will still work, although a
+ deprecation warning will be issued.
+
+Backwards Incompatibilities
+---------------------------
+
+- If your code used the ITraverser interface directly (not via an API
+ function such as ``find_model``) via an adapter lookup, you'll need
+ to change your code to expect a dictionary rather than a 3- or
+ 6-tuple if your code ever gets return values from the default
+ ModelGraphTraverser or RoutesModelTraverser adapters.
0.8a7 (2009-05-16)
==================
diff --git a/docs/api/traversal.rst b/docs/api/traversal.rst
index 89d9135a0..2f57fd3aa 100644
--- a/docs/api/traversal.rst
+++ b/docs/api/traversal.rst
@@ -19,6 +19,8 @@
.. autofunction:: virtual_root
+ .. autofunction:: traverse
+
.. note:: A function named ``model_url`` used to be present in this
module. It was moved to :ref:`url_module` in version 0.6.1.
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index 5dadd252a..cecc3a397 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -59,20 +59,21 @@ deprecated('IRootPolicy',
class ITraverser(Interface):
def __call__(environ):
- """ Return a tuple in the form ``(context, view_name, subpath,
- traversed, virtual_root, virtual_root_path)`` , typically the
- result of an object graph traversal. ``context`` will be a
- model object, ``view_name`` will be the view name used (a
- Unicode name), ``subpath`` will be a sequence of Unicode names
- that followed the view name but were not traversed,
- ``traversed`` will be a sequence of Unicode names that were
- traversed (including the virtual root path, if any) or
- ``None`` if no traversal was performed, ``virtual_root`` will
- be a model object representing the virtual root (or the
- physical root if traversal was not performed), and
- ``virtual_root_path`` will be a sequence representing the
- virtual root path (a sequence of Unicode names) or None if
- traversal was not performed."""
+ """ Return a dictionary with the keys ``root``, ``context``,
+ ``view_name``, ``subpath``, ``traversed``, ``vroot``, and
+ ``vroot_path``. These values are typically the result of an
+ object graph traversal. ``root`` is the physical root object,
+ ``context`` will be a model object, ``view_name`` will be the
+ view name used (a Unicode name), ``subpath`` will be a
+ sequence of Unicode names that followed the view name but were
+ not traversed, ``traversed`` will be a sequence of Unicode
+ names that were traversed (including the virtual root path, if
+ any) or ``None`` if no traversal was performed,
+ ``virtual_root`` will be a model object representing the
+ virtual root (or the physical root if traversal was not
+ performed), and ``virtual_root_path`` will be a sequence
+ representing the virtual root path (a sequence of Unicode
+ names) or None if traversal was not performed."""
class ITraverserFactory(Interface):
def __call__(context):
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index 5278da892..614d32aa2 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -15,7 +15,6 @@ from repoze.bfg.interfaces import IRequestFactory
from repoze.bfg.interfaces import IRootFactory
from repoze.bfg.interfaces import IRouter
from repoze.bfg.interfaces import IRoutesMapper
-from repoze.bfg.interfaces import ITraverserFactory
from repoze.bfg.interfaces import ISecurityPolicy
from repoze.bfg.interfaces import ISettings
from repoze.bfg.interfaces import IUnauthorizedAppFactory
@@ -35,6 +34,7 @@ from repoze.bfg.settings import Settings
from repoze.bfg.urldispatch import RoutesRootFactory
+from repoze.bfg.traversal import _traverse
from repoze.bfg.view import _view_execution_permitted
from repoze.bfg.wsgi import Unauthorized
from repoze.bfg.wsgi import NotFound
@@ -99,25 +99,15 @@ class Router(object):
registry.has_listeners and registry.notify(NewRequest(request))
root = self.root_factory(environ)
- traverser = registry.getAdapter(root, ITraverserFactory)
- vals = traverser(environ)
-
- try:
- context, view_name, subpath, traversed, vroot, vroot_path = vals
- except ValueError:
- if not (traverser.__class__ in self.traverser_warned):
- self.logger and self.logger.warn(
- '%s is an pre-0.7.1-style ITraverser returning only '
- '3 arguments; please update it to the new '
- '6-argument-returning interface for improved '
- 'functionality. See the repoze.bfg.interfaces module '
- 'for the new ITraverser interface '
- 'definition' % traverser)
- self.traverser_warned[traverser.__class__] = True
- context, view_name, subpath = vals
- traversed = []
- vroot = root
- vroot_path = []
+ tdict = _traverse(root, environ)
+ if '_deprecation_warning' in tdict:
+ warning = tdict.pop('_deprecation_warning')
+ if not warning in self.traverser_warned:
+ self.logger and self.logger.warn(warning)
+ context, view_name, subpath, traversed, vroot, vroot_path = (
+ tdict['context'], tdict['view_name'], tdict['subpath'],
+ tdict['traversed'], tdict['virtual_root'],
+ tdict['virtual_root_path'])
if isinstance(request, WebObRequest):
# webob.Request's __setattr__ (as of 0.9.5 and lower)
@@ -125,13 +115,7 @@ class Router(object):
# webob.Request, go around its back and set stuff into
# the environ directly
attrs = environ.setdefault('webob.adhoc_attrs', {})
- attrs['root'] = root
- attrs['context'] = context
- attrs['view_name'] = view_name
- attrs['subpath'] = subpath
- attrs['traversed'] = traversed
- attrs['virtual_root'] = vroot
- attrs['virtual_root_path'] = vroot_path
+ attrs.update(tdict)
else:
request.root = root
request.context = context
@@ -181,9 +165,9 @@ class Router(object):
msg = (
'debug_notfound of url %s; path_info: %r, context: %r, '
'view_name: %r, subpath: %r, traversed: %r, '
- 'vroot: %r, vroot_path: %r' % (
+ 'root: %r, vroot: %r, vroot_path: %r' % (
request.url, request.path_info, context, view_name,
- subpath, traversed, vroot, vroot_path)
+ subpath, traversed, root, vroot, vroot_path)
)
logger and logger.debug(msg)
else:
diff --git a/repoze/bfg/testing.py b/repoze/bfg/testing.py
index 0ff1dbade..d4ff6fe4d 100644
--- a/repoze/bfg/testing.py
+++ b/repoze/bfg/testing.py
@@ -205,7 +205,10 @@ def make_traverser_factory(root):
def __call__(self, environ):
path = environ['PATH_INFO']
ob = root[path]
- return ob, '', []
+ from repoze.bfg.traversal import traversal_path
+ traversed = list(traversal_path(path))
+ return {'context':ob, 'view_name':'','subpath':[],
+ 'traversed':traversed, 'vroot':ob, 'vroot_path':[]}
return DummyTraverserFactory
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index 95defb7e7..db47f832e 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -38,9 +38,37 @@ class RouterTests(unittest.TestCase):
settings = Settings(**defaultkw)
self.registry.registerUtility(settings, ISettings)
- def _registerTraverserFactory(self, app, name, *for_):
+ def _registerTraverserFactory(self, context, view_name='', subpath=None,
+ traversed=None, virtual_root=None,
+ virtual_root_path=None, **kw):
from repoze.bfg.interfaces import ITraverserFactory
- self.registry.registerAdapter(app, for_, ITraverserFactory, name)
+
+ if virtual_root is None:
+ virtual_root = context
+ if subpath is None:
+ subpath = []
+ if traversed is None:
+ traversed = []
+ if virtual_root_path is None:
+ virtual_root_path = []
+
+ class DummyTraverserFactory:
+ def __init__(self, root):
+ self.root = root
+
+ def __call__(self, path):
+ values = {'root':self.root,
+ 'context':context,
+ 'view_name':view_name,
+ 'subpath':subpath,
+ 'traversed':traversed,
+ 'virtual_root':virtual_root,
+ 'virtual_root_path':virtual_root_path}
+ kw.update(values)
+ return kw
+
+ self.registry.registerAdapter(DummyTraverserFactory, (None,),
+ ITraverserFactory, name='')
def _registerView(self, app, name, *for_):
from repoze.bfg.interfaces import IView
@@ -79,6 +107,7 @@ class RouterTests(unittest.TestCase):
'SERVER_NAME':'localhost',
'SERVER_PORT':'8080',
'REQUEST_METHOD':'GET',
+ 'PATH_INFO':'/',
}
environ.update(extras)
return environ
@@ -87,41 +116,16 @@ class RouterTests(unittest.TestCase):
rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
- self._registerTraverserFactory(traversalfactory, '', None)
- logger = self._registerLogger()
+ self._registerTraverserFactory(context)
self._registerRootFactory(rootfactory)
router = self._makeOne()
self.assertEqual(router.root_policy, rootfactory)
- def test_3arg_policy(self):
- rootfactory = make_rootfactory(None)
- environ = self._makeEnviron()
- context = DummyContext()
- traversalfactory = make_3arg_traversal_factory(context, '', [])
- self._registerTraverserFactory(traversalfactory, '', None)
- logger = self._registerLogger()
- self._registerRootFactory(rootfactory)
- router = self._makeOne()
- start_response = DummyStartResponse()
- result = router(environ, start_response)
- self.failUnless(traversalfactory in router.traverser_warned)
- headers = start_response.headers
- self.assertEqual(len(headers), 2)
- status = start_response.status
- self.assertEqual(status, '404 Not Found')
- self.failUnless('http://localhost:8080' in result[0], result)
- self.failIf('debug_notfound' in result[0])
- self.assertEqual(len(logger.messages), 1)
- message = logger.messages[0]
- self.failUnless('is an pre-0.7.1-style ITraverser ' in message)
-
def test_call_no_view_registered_no_isettings(self):
rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
- self._registerTraverserFactory(traversalfactory, '', None)
+ self._registerTraverserFactory(context)
logger = self._registerLogger()
self._registerRootFactory(rootfactory)
router = self._makeOne()
@@ -141,8 +145,7 @@ class RouterTests(unittest.TestCase):
class NotFound(object):
implements(IContextNotFound)
context = NotFound()
- traversalfactory = make_traversal_factory(context, '', [])
- self._registerTraverserFactory(traversalfactory, '', None)
+ self._registerTraverserFactory(context)
environ = self._makeEnviron()
start_response = DummyStartResponse()
rootfactory = make_rootfactory(NotFound())
@@ -157,8 +160,7 @@ class RouterTests(unittest.TestCase):
rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
- self._registerTraverserFactory(traversalfactory, '', None)
+ self._registerTraverserFactory(context)
logger = self._registerLogger()
self._registerSettings(debug_notfound=False)
self._registerRootFactory(rootfactory)
@@ -177,8 +179,7 @@ class RouterTests(unittest.TestCase):
rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
- self._registerTraverserFactory(traversalfactory, '', None)
+ self._registerTraverserFactory(context)
self._registerSettings(debug_notfound=True)
logger = self._registerLogger()
self._registerRootFactory(rootfactory)
@@ -190,7 +191,7 @@ class RouterTests(unittest.TestCase):
status = start_response.status
self.assertEqual(status, '404 Not Found')
self.failUnless(
- "debug_notfound of url http://localhost:8080; path_info: '', "
+ "debug_notfound of url http://localhost:8080/; path_info: '/', "
"context:" in result[0])
self.failUnless(
"view_name: '', subpath: []" in result[0])
@@ -198,7 +199,7 @@ class RouterTests(unittest.TestCase):
self.assertEqual(len(logger.messages), 1)
message = logger.messages[0]
self.failUnless('of url http://localhost:8080' in message)
- self.failUnless("path_info: ''" in message)
+ self.failUnless("path_info: '/'" in message)
self.failUnless('DummyContext instance at' in message)
self.failUnless("view_name: ''" in message)
self.failUnless("subpath: []" in message)
@@ -206,9 +207,8 @@ class RouterTests(unittest.TestCase):
def test_call_view_returns_nonresponse(self):
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(context)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
view = make_view('abc')
self._registerView(view, '', None, None)
self._registerRootFactory(rootfactory)
@@ -219,12 +219,11 @@ class RouterTests(unittest.TestCase):
def test_call_view_registered_nonspecific_default_path(self):
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(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()
@@ -238,15 +237,34 @@ class RouterTests(unittest.TestCase):
self.assertEqual(environ['webob.adhoc_attrs']['context'], context)
self.assertEqual(environ['webob.adhoc_attrs']['root'], None)
+ def test_call_deprecation_warning(self):
+ rootfactory = make_rootfactory(None)
+ context = DummyContext()
+ self._registerTraverserFactory(context, _deprecation_warning='abc')
+ response = DummyResponse()
+ response.app_iter = ['Hello world']
+ view = make_view(response)
+ environ = self._makeEnviron()
+ self._registerView(view, '', None, None)
+ self._registerRootFactory(rootfactory)
+ router = self._makeOne()
+ logger = self._registerLogger()
+ router.logger = logger
+ start_response = DummyStartResponse()
+ router(environ, start_response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0], 'abc')
+
def test_call_view_registered_nonspecific_nondefault_path_and_subpath(self):
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, 'foo', ['bar'])
+ self._registerTraverserFactory(context, view_name='foo',
+ subpath=['bar'],
+ traversed=['context'])
response = DummyResponse()
response.app_iter = ['Hello world']
view = make_view(response)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, 'foo', None, None)
self._registerRootFactory(rootfactory)
router = self._makeOne()
@@ -269,12 +287,11 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
context = DummyContext()
directlyProvides(context, IContext)
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(context)
response = DummyResponse()
response.app_iter = ['Hello world']
view = make_view(response)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', IContext, IRequest)
self._registerRootFactory(rootfactory)
router = self._makeOne()
@@ -299,11 +316,10 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
context = DummyContext()
directlyProvides(context, INotContext)
- traversalfactory = make_traversal_factory(context, '', [''])
+ self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', IContext, IRequest)
self._registerRootFactory(rootfactory)
router = self._makeOne()
@@ -321,11 +337,10 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
context = DummyContext()
directlyProvides(context, IContext)
- traversalfactory = make_traversal_factory(context, '', [''])
+ self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', IContext, IRequest)
secpol = DummySecurityPolicy()
self._registerSecurityPolicy(secpol)
@@ -344,13 +359,12 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
context = DummyContext()
directlyProvides(context, IContext)
- traversalfactory = make_traversal_factory(context, '', [''])
+ self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
secpol = DummySecurityPolicy()
permissionfactory = make_permission_factory(True)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', IContext, IRequest)
self._registerSecurityPolicy(secpol)
self._registerPermission(permissionfactory, '', IContext, IRequest)
@@ -370,7 +384,7 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
context = DummyContext()
directlyProvides(context, IContext)
- traversalfactory = make_traversal_factory(context, '', [''])
+ self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
secpol = DummySecurityPolicy()
@@ -379,7 +393,6 @@ class RouterTests(unittest.TestCase):
ACLDenied('ace', 'acl', 'permission', ['principals'], context)
)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', IContext, IRequest)
self._registerSecurityPolicy(secpol)
self._registerPermission(permissionfactory, '', IContext, IRequest)
@@ -401,7 +414,7 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
context = DummyContext()
directlyProvides(context, IContext)
- traversalfactory = make_traversal_factory(context, '', [''])
+ self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
secpol = DummySecurityPolicy()
@@ -410,7 +423,6 @@ class RouterTests(unittest.TestCase):
ACLDenied('ace', 'acl', 'permission', ['principals'], context)
)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', IContext, IRequest)
self._registerSecurityPolicy(secpol)
self._registerPermission(permissionfactory, '', IContext, IRequest)
@@ -433,7 +445,7 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
context = DummyContext()
directlyProvides(context, IContext)
- traversalfactory = make_traversal_factory(context, '', [''])
+ self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
secpol = DummySecurityPolicy()
@@ -442,7 +454,6 @@ class RouterTests(unittest.TestCase):
ACLDenied('ace', 'acl', 'permission', ['principals'], context)
)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', IContext, IRequest)
self._registerSecurityPolicy(secpol)
self._registerPermission(permissionfactory, '', IContext, IRequest)
@@ -462,7 +473,7 @@ class RouterTests(unittest.TestCase):
self.assertEqual(len(logger.messages), 1)
logged = logger.messages[0]
self.failUnless(
- "debug_authorization of url http://localhost:8080 (view name "
+ "debug_authorization of url http://localhost:8080/ (view name "
"'' against context" in logged)
self.failUnless(
"ACLDenied permission 'permission' via ACE 'ace' in ACL 'acl' on "
@@ -473,12 +484,11 @@ class RouterTests(unittest.TestCase):
def test_call_eventsends(self):
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(context)
response = DummyResponse()
response.app_iter = ['Hello world']
view = make_view(response)
environ = self._makeEnviron()
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', None, None)
from repoze.bfg.interfaces import INewRequest
from repoze.bfg.interfaces import INewResponse
@@ -500,12 +510,11 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(context)
response = DummyResponse()
response.app_iter = ['Hello world']
view = make_view(response)
environ = self._makeEnviron(REQUEST_METHOD='POST')
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', None, None)
self._registerRootFactory(rootfactory)
router = self._makeOne()
@@ -524,12 +533,11 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(context)
response = DummyResponse()
response.app_iter = ['Hello world']
view = make_view(response)
environ = self._makeEnviron(REQUEST_METHOD='PUT')
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', None, None)
self._registerRootFactory(rootfactory)
router = self._makeOne()
@@ -546,12 +554,11 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequest
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(context)
response = DummyResponse()
response.app_iter = ['Hello world']
view = make_view(response)
environ = self._makeEnviron(REQUEST_METHOD='UNKNOWN')
- self._registerTraverserFactory(traversalfactory, '', None)
self._registerView(view, '', None, None)
self._registerRootFactory(rootfactory)
router = self._makeOne()
@@ -568,12 +575,11 @@ class RouterTests(unittest.TestCase):
self.registry.registerUtility(DummyRequest, IRequestFactory)
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(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()
@@ -594,12 +600,11 @@ class RouterTests(unittest.TestCase):
self.registry.registerUtility(app, INotFoundAppFactory)
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(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()
@@ -612,12 +617,11 @@ class RouterTests(unittest.TestCase):
self.registry.registerUtility(app, IUnauthorizedAppFactory)
rootfactory = make_rootfactory(None)
context = DummyContext()
- traversalfactory = make_traversal_factory(context, '', [])
+ self._registerTraverserFactory(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()
@@ -740,26 +744,6 @@ def make_view(response):
return response
return view
-def make_traversal_factory(context, name, subpath, vroot=None,
- vroot_path=(), traversed=()):
- class DummyTraversalFactory:
- def __init__(self, root):
- self.root = root
-
- def __call__(self, path):
- return context, name, subpath, traversed, vroot, vroot_path
- return DummyTraversalFactory
-
-def make_3arg_traversal_factory(context, name, subpath):
- class DummyTraversalFactory:
- def __init__(self, root):
- self.root = root
-
- def __call__(self, path):
- return context, name, subpath
- return DummyTraversalFactory
-
-
def make_permission_factory(result):
class DummyPermissionFactory:
def __init__(self, context, request):
diff --git a/repoze/bfg/tests/test_testing.py b/repoze/bfg/tests/test_testing.py
index d61092065..504151ce2 100644
--- a/repoze/bfg/tests/test_testing.py
+++ b/repoze/bfg/tests/test_testing.py
@@ -41,8 +41,20 @@ class TestTestingFunctions(unittest.TestCase):
from zope.component import getAdapter
from repoze.bfg.interfaces import ITraverserFactory
adapter = getAdapter(None, ITraverserFactory)
- self.assertEqual(adapter({'PATH_INFO':'/ob1'}), (ob1, '', []))
- self.assertEqual(adapter({'PATH_INFO':'/ob2'}), (ob2, '', []))
+ result = adapter({'PATH_INFO':'/ob1'})
+ self.assertEqual(result['context'], ob1)
+ self.assertEqual(result['view_name'], '')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], [u'ob1'])
+ self.assertEqual(result['vroot'], ob1)
+ self.assertEqual(result['vroot_path'], [])
+ result = adapter({'PATH_INFO':'/ob2'})
+ self.assertEqual(result['context'], ob2)
+ self.assertEqual(result['view_name'], '')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], [u'ob2'])
+ self.assertEqual(result['vroot'], ob2)
+ self.assertEqual(result['vroot_path'], [])
self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'})
from repoze.bfg.traversal import find_model
self.assertEqual(find_model(None, '/ob1'), ob1)
diff --git a/repoze/bfg/tests/test_traversal.py b/repoze/bfg/tests/test_traversal.py
index e0ab8d938..41cf667b9 100644
--- a/repoze/bfg/tests/test_traversal.py
+++ b/repoze/bfg/tests/test_traversal.py
@@ -55,7 +55,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
def tearDown(self):
cleanUp()
-
+
def _getTargetClass(self):
from repoze.bfg.traversal import ModelGraphTraverser
return ModelGraphTraverser
@@ -83,75 +83,75 @@ class ModelGraphTraverserTests(unittest.TestCase):
def test_call_with_no_pathinfo(self):
policy = self._makeOne(None)
environ = self._getEnviron()
- ctx, name, subpath, traversed, vroot, vroot_path = policy(environ)
- self.assertEqual(ctx, None)
- self.assertEqual(name, '')
- self.assertEqual(subpath, [])
- self.assertEqual(traversed, [])
- self.assertEqual(vroot, policy.root)
- self.assertEqual(vroot_path, [])
+ result = policy(environ)
+ self.assertEqual(result['context'], None)
+ self.assertEqual(result['view_name'], '')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], [])
+ self.assertEqual(result['virtual_root'], policy.root)
+ self.assertEqual(result['virtual_root_path'], [])
def test_call_pathel_with_no_getitem(self):
policy = self._makeOne(None)
environ = self._getEnviron(PATH_INFO='/foo/bar')
- ctx, name, subpath, traversed, vroot, vroot_path = policy(environ)
- self.assertEqual(ctx, None)
- self.assertEqual(name, 'foo')
- self.assertEqual(subpath, ['bar'])
- self.assertEqual(traversed, [])
- self.assertEqual(vroot, policy.root)
- self.assertEqual(vroot_path, [])
+ result = policy(environ)
+ self.assertEqual(result['context'], None)
+ self.assertEqual(result['view_name'], 'foo')
+ self.assertEqual(result['subpath'], ['bar'])
+ self.assertEqual(result['traversed'], [])
+ self.assertEqual(result['virtual_root'], policy.root)
+ self.assertEqual(result['virtual_root_path'], [])
def test_call_withconn_getitem_emptypath_nosubpath(self):
root = DummyContext()
policy = self._makeOne(root)
environ = self._getEnviron(PATH_INFO='')
- ctx, name, subpath, traversed, vroot, vroot_path = policy(environ)
- self.assertEqual(ctx, root)
- self.assertEqual(name, '')
- self.assertEqual(subpath, [])
- self.assertEqual(traversed, [])
- self.assertEqual(vroot, root)
- self.assertEqual(vroot_path, [])
+ result = policy(environ)
+ self.assertEqual(result['context'], root)
+ self.assertEqual(result['view_name'], '')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], [])
+ self.assertEqual(result['virtual_root'], root)
+ self.assertEqual(result['virtual_root_path'], [])
def test_call_withconn_getitem_withpath_nosubpath(self):
foo = DummyContext()
root = DummyContext(foo)
policy = self._makeOne(root)
environ = self._getEnviron(PATH_INFO='/foo/bar')
- ctx, name, subpath, traversed, vroot, vroot_path = policy(environ)
- self.assertEqual(ctx, foo)
- self.assertEqual(name, 'bar')
- self.assertEqual(subpath, [])
- self.assertEqual(traversed, [u'foo'])
- self.assertEqual(vroot, root)
- self.assertEqual(vroot_path, [])
+ result = policy(environ)
+ self.assertEqual(result['context'], foo)
+ self.assertEqual(result['view_name'], 'bar')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], [u'foo'])
+ self.assertEqual(result['virtual_root'], root)
+ self.assertEqual(result['virtual_root_path'], [])
def test_call_withconn_getitem_withpath_withsubpath(self):
foo = DummyContext()
root = DummyContext(foo)
policy = self._makeOne(root)
environ = self._getEnviron(PATH_INFO='/foo/bar/baz/buz')
- ctx, name, subpath, traversed, vroot, vroot_path = policy(environ)
- self.assertEqual(ctx, foo)
- self.assertEqual(name, 'bar')
- self.assertEqual(subpath, ['baz', 'buz'])
- self.assertEqual(traversed, [u'foo'])
- self.assertEqual(vroot, root)
- self.assertEqual(vroot_path, [])
+ result = policy(environ)
+ self.assertEqual(result['context'], foo)
+ self.assertEqual(result['view_name'], 'bar')
+ self.assertEqual(result['subpath'], ['baz', 'buz'])
+ self.assertEqual(result['traversed'], [u'foo'])
+ self.assertEqual(result['virtual_root'], root)
+ self.assertEqual(result['virtual_root_path'], [])
def test_call_with_explicit_viewname(self):
foo = DummyContext()
root = DummyContext(foo)
policy = self._makeOne(root)
environ = self._getEnviron(PATH_INFO='/@@foo')
- ctx, name, subpath, traversed, vroot, vroot_path = policy(environ)
- self.assertEqual(ctx, root)
- self.assertEqual(name, 'foo')
- self.assertEqual(subpath, [])
- self.assertEqual(traversed, [])
- self.assertEqual(vroot, root)
- self.assertEqual(vroot_path, [])
+ result = policy(environ)
+ self.assertEqual(result['context'], root)
+ self.assertEqual(result['view_name'], 'foo')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], [])
+ self.assertEqual(result['virtual_root'], root)
+ self.assertEqual(result['virtual_root_path'], [])
def test_call_with_vh_root(self):
environ = self._getEnviron(PATH_INFO='/baz',
@@ -165,13 +165,13 @@ class ModelGraphTraverserTests(unittest.TestCase):
root = DummyContext(foo)
root.name = 'root'
policy = self._makeOne(root)
- ctx, name, subpath, traversed, vroot, vroot_path = policy(environ)
- self.assertEqual(ctx, baz)
- self.assertEqual(name, '')
- self.assertEqual(subpath, [])
- self.assertEqual(traversed, [u'foo', u'bar', u'baz'])
- self.assertEqual(vroot, bar)
- self.assertEqual(vroot_path, [u'foo', u'bar'])
+ result = policy(environ)
+ self.assertEqual(result['context'], baz)
+ self.assertEqual(result['view_name'], '')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], [u'foo', u'bar', u'baz'])
+ self.assertEqual(result['virtual_root'], bar)
+ self.assertEqual(result['virtual_root_path'], [u'foo', u'bar'])
def test_non_utf8_path_segment_unicode_path_segments_fails(self):
foo = DummyContext()
@@ -245,7 +245,7 @@ class FindModelTests(unittest.TestCase):
def test_list(self):
model = DummyContext()
- traverser = make_traverser(model, '', [])
+ traverser = make_traverser({'context':model, 'view_name':''})
self._registerTraverserFactory(traverser)
result = self._callFUT(model, [''])
self.assertEqual(result, model)
@@ -253,7 +253,7 @@ class FindModelTests(unittest.TestCase):
def test_generator(self):
model = DummyContext()
- traverser = make_traverser(model, '', [])
+ traverser = make_traverser({'context':model, 'view_name':''})
self._registerTraverserFactory(traverser)
def foo():
yield ''
@@ -263,7 +263,7 @@ class FindModelTests(unittest.TestCase):
def test_self_string_found(self):
model = DummyContext()
- traverser = make_traverser(model, '', [])
+ traverser = make_traverser({'context':model, 'view_name':''})
self._registerTraverserFactory(traverser)
result = self._callFUT(model, '')
self.assertEqual(result, model)
@@ -271,7 +271,7 @@ class FindModelTests(unittest.TestCase):
def test_self_tuple_found(self):
model = DummyContext()
- traverser = make_traverser(model, '', [])
+ traverser = make_traverser({'context':model, 'view_name':''})
self._registerTraverserFactory(traverser)
result = self._callFUT(model, ())
self.assertEqual(result, model)
@@ -280,7 +280,7 @@ class FindModelTests(unittest.TestCase):
def test_relative_string_found(self):
model = DummyContext()
baz = DummyContext()
- traverser = make_traverser(baz, '', [])
+ traverser = make_traverser({'context':baz, 'view_name':''})
self._registerTraverserFactory(traverser)
result = self._callFUT(model, 'baz')
self.assertEqual(result, baz)
@@ -289,7 +289,7 @@ class FindModelTests(unittest.TestCase):
def test_relative_tuple_found(self):
model = DummyContext()
baz = DummyContext()
- traverser = make_traverser(baz, '', [])
+ traverser = make_traverser({'context':baz, 'view_name':''})
self._registerTraverserFactory(traverser)
result = self._callFUT(model, ('baz',))
self.assertEqual(result, baz)
@@ -298,7 +298,7 @@ class FindModelTests(unittest.TestCase):
def test_relative_string_notfound(self):
model = DummyContext()
baz = DummyContext()
- traverser = make_traverser(baz, 'bar', [])
+ traverser = make_traverser({'context':baz, 'view_name':'bar'})
self._registerTraverserFactory(traverser)
self.assertRaises(KeyError, self._callFUT, model, 'baz')
self.assertEqual(model.environ['PATH_INFO'], 'baz')
@@ -306,7 +306,7 @@ class FindModelTests(unittest.TestCase):
def test_relative_tuple_notfound(self):
model = DummyContext()
baz = DummyContext()
- traverser = make_traverser(baz, 'bar', [])
+ traverser = make_traverser({'context':baz, 'view_name':'bar'})
self._registerTraverserFactory(traverser)
self.assertRaises(KeyError, self._callFUT, model, ('baz',))
self.assertEqual(model.environ['PATH_INFO'], 'baz')
@@ -316,7 +316,7 @@ class FindModelTests(unittest.TestCase):
model = DummyContext()
model.__parent__ = root
model.__name__ = 'baz'
- traverser = make_traverser(root, '', [])
+ traverser = make_traverser({'context':root, 'view_name':''})
self._registerTraverserFactory(traverser)
result = self._callFUT(model, '/')
self.assertEqual(result, root)
@@ -328,7 +328,7 @@ class FindModelTests(unittest.TestCase):
model = DummyContext()
model.__parent__ = root
model.__name__ = 'baz'
- traverser = make_traverser(root, '', [])
+ traverser = make_traverser({'context':root, 'view_name':''})
self._registerTraverserFactory(traverser)
result = self._callFUT(model, ('',))
self.assertEqual(result, root)
@@ -340,7 +340,7 @@ class FindModelTests(unittest.TestCase):
model = DummyContext()
model.__parent__ = root
model.__name__ = 'baz'
- traverser = make_traverser(root, 'fuz', [])
+ traverser = make_traverser({'context':root, 'view_name':'fuz'})
self._registerTraverserFactory(traverser)
self.assertRaises(KeyError, self._callFUT, model, '/')
self.assertEqual(root.wascontext, True)
@@ -351,12 +351,13 @@ class FindModelTests(unittest.TestCase):
model = DummyContext()
model.__parent__ = root
model.__name__ = 'baz'
- traverser = make_traverser(root, 'fuz', [])
+ traverser = make_traverser({'context':root, 'view_name':'fuz'})
self._registerTraverserFactory(traverser)
self.assertRaises(KeyError, self._callFUT, model, ('',))
self.assertEqual(root.wascontext, True)
self.assertEqual(root.environ['PATH_INFO'], '/')
+
class ModelPathTests(unittest.TestCase):
def _callFUT(self, model, *elements):
from repoze.bfg.traversal import model_path
@@ -569,7 +570,7 @@ class TraversalContextURLTests(unittest.TestCase):
self.assertEqual(result,
'http://example.com:5432/La%20Pe%C3%B1a/La%20Pe%C3%B1a/')
- def test_call_with_vroot_path(self):
+ def test_call_with_virtual_root_path(self):
from repoze.bfg.interfaces import VH_ROOT_KEY
root = DummyContext()
root.__parent__ = None
@@ -590,7 +591,7 @@ class TraversalContextURLTests(unittest.TestCase):
result = context_url()
self.assertEqual(result, 'http://example.com:5432/')
- def test_virtual_root_no_vroot_path(self):
+ def test_virtual_root_no_virtual_root_path(self):
root = DummyContext()
root.__name__ = None
root.__parent__ = None
@@ -601,7 +602,7 @@ class TraversalContextURLTests(unittest.TestCase):
context_url = self._makeOne(one, request)
self.assertEqual(context_url.virtual_root(), root)
- def test_virtual_root_no_vroot_path_with_root_on_request(self):
+ def test_virtual_root_no_virtual_root_path_with_root_on_request(self):
context = DummyContext()
context.__parent__ = None
request = DummyRequest()
@@ -609,14 +610,14 @@ class TraversalContextURLTests(unittest.TestCase):
context_url = self._makeOne(context, request)
self.assertEqual(context_url.virtual_root(), request.root)
- def test_virtual_root_with_vroot_path(self):
+ def test_virtual_root_with_virtual_root_path(self):
from repoze.bfg.interfaces import VH_ROOT_KEY
context = DummyContext()
context.__parent__ = None
traversed_to = DummyContext()
environ = {VH_ROOT_KEY:'/one'}
request = DummyRequest(environ)
- traverser = make_traverser(traversed_to, '', [])
+ traverser = make_traverser({'context':traversed_to, 'view_name':''})
self._registerTraverserFactory(traverser)
context_url = self._makeOne(context, request)
self.assertEqual(context_url.virtual_root(), traversed_to)
@@ -661,14 +662,157 @@ class TestVirtualRoot(unittest.TestCase):
result = self._callFUT(context, request)
self.assertEqual(result, '123')
-def make_traverser(*args):
+class TraverseTests(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, context, name):
+ from repoze.bfg.traversal import traverse
+ return traverse(context, name)
+
+ def _registerTraverserFactory(self, traverser):
+ import zope.component
+ gsm = zope.component.getGlobalSiteManager()
+ from repoze.bfg.interfaces import ITraverserFactory
+ from zope.interface import Interface
+ gsm.registerAdapter(traverser, (Interface,), ITraverserFactory)
+
+ def test_list(self):
+ model = DummyContext()
+ traverser = make_traverser({'context':model, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ self._callFUT(model, [''])
+ self.assertEqual(model.environ['PATH_INFO'], '/')
+
+ def test_generator(self):
+ model = DummyContext()
+ traverser = make_traverser({'context':model, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ def foo():
+ yield ''
+ self._callFUT(model, foo())
+ self.assertEqual(model.environ['PATH_INFO'], '/')
+
+ def test_self_string_found(self):
+ model = DummyContext()
+ traverser = make_traverser({'context':model, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ self._callFUT(model, '')
+ self.assertEqual(model.environ['PATH_INFO'], '')
+
+ def test_self_tuple_found(self):
+ model = DummyContext()
+ traverser = make_traverser({'context':model, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ self._callFUT(model, ())
+ self.assertEqual(model.environ['PATH_INFO'], '')
+
+ def test_relative_string_found(self):
+ model = DummyContext()
+ baz = DummyContext()
+ traverser = make_traverser({'context':baz, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ self._callFUT(model, 'baz')
+ self.assertEqual(model.environ['PATH_INFO'], 'baz')
+
+ def test_relative_tuple_found(self):
+ model = DummyContext()
+ baz = DummyContext()
+ traverser = make_traverser({'context':baz, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ self._callFUT(model, ('baz',))
+ self.assertEqual(model.environ['PATH_INFO'], 'baz')
+
+ def test_absolute_string_found(self):
+ root = DummyContext()
+ model = DummyContext()
+ model.__parent__ = root
+ model.__name__ = 'baz'
+ traverser = make_traverser({'context':root, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ self._callFUT(model, '/')
+ self.assertEqual(root.wascontext, True)
+ self.assertEqual(root.environ['PATH_INFO'], '/')
+
+ def test_absolute_tuple_found(self):
+ root = DummyContext()
+ model = DummyContext()
+ model.__parent__ = root
+ model.__name__ = 'baz'
+ traverser = make_traverser({'context':root, 'view_name':''})
+ self._registerTraverserFactory(traverser)
+ self._callFUT(model, ('',))
+ self.assertEqual(root.wascontext, True)
+ self.assertEqual(root.environ['PATH_INFO'], '/')
+
+class UnderTraverseTests(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, context, environ):
+ from repoze.bfg.traversal import _traverse
+ return _traverse(context, environ)
+
+ def _registerTraverserFactory(self, traverser):
+ import zope.component
+ gsm = zope.component.getGlobalSiteManager()
+ from repoze.bfg.interfaces import ITraverserFactory
+ from zope.interface import Interface
+ gsm.registerAdapter(traverser, (Interface,), ITraverserFactory)
+
+ def test_isdict(self):
+ traverser = make_traverser({})
+ self._registerTraverserFactory(traverser)
+ context = DummyContext()
+ result = self._callFUT(context, None)
+ self.assertEqual(result, {})
+
+ def test_issixtuple(self):
+ traverser = make_traverser((1,2,3,4,5,6))
+ self._registerTraverserFactory(traverser)
+ context = DummyContext()
+ result = self._callFUT(context, None)
+ self.assertEqual(result['context'], 1)
+ self.assertEqual(result['view_name'], 2)
+ self.assertEqual(result['subpath'], 3)
+ self.assertEqual(result['traversed'], 4)
+ self.assertEqual(result['virtual_root'], 5)
+ self.assertEqual(result['virtual_root_path'], 6)
+ self.assertEqual(result['root'], None)
+ self.failUnless(result['_deprecation_warning'].startswith(
+ "<class 'repoze.bfg.tests.test_traversal.DummyTraverser'>"))
+ self.failUnless("6-argument tuple" in result['_deprecation_warning'])
+
+ def test_isthreetuple(self):
+ traverser = make_traverser((1,2,3))
+ self._registerTraverserFactory(traverser)
+ context = DummyContext()
+ result = self._callFUT(context, None)
+ self.assertEqual(result['context'], 1)
+ self.assertEqual(result['view_name'], 2)
+ self.assertEqual(result['subpath'], 3)
+ self.assertEqual(result['traversed'], None)
+ self.assertEqual(result['virtual_root'], None)
+ self.assertEqual(result['virtual_root_path'], None)
+ self.assertEqual(result['root'], None)
+ self.failUnless(result['_deprecation_warning'].startswith(
+ "<class 'repoze.bfg.tests.test_traversal.DummyTraverser'>"))
+ self.failUnless("3-argument tuple" in result['_deprecation_warning'])
+
+def make_traverser(result):
class DummyTraverser(object):
def __init__(self, context):
self.context = context
context.wascontext = True
def __call__(self, environ):
self.context.environ = environ
- return args
+ return result
return DummyTraverser
class DummyContext(object):
diff --git a/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py
index e5ae483e7..92564146e 100644
--- a/repoze/bfg/tests/test_urldispatch.py
+++ b/repoze/bfg/tests/test_urldispatch.py
@@ -232,12 +232,12 @@ class RoutesModelTraverserTests(unittest.TestCase):
route = DummyRoute('yo')
environ = {'wsgiorg.routing_args': routing_args, 'bfg.route': route}
result = traverser(environ)
- self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'yo')
- self.assertEqual(result[2], [])
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], model)
- self.assertEqual(result[5], None)
+ self.assertEqual(result['context'], model)
+ self.assertEqual(result['view_name'], 'yo')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], None)
+ self.assertEqual(result['virtual_root'], model)
+ self.assertEqual(result['virtual_root_path'], None)
def test_call_with_subpath(self):
model = DummyContext()
@@ -246,12 +246,12 @@ class RoutesModelTraverserTests(unittest.TestCase):
route = DummyRoute('yo')
environ = {'wsgiorg.routing_args':routing_args, 'bfg.route': route}
result = traverser(environ)
- self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'yo')
- self.assertEqual(result[2], ['a', 'b','c'])
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], model)
- self.assertEqual(result[5], None)
+ self.assertEqual(result['context'], model)
+ self.assertEqual(result['view_name'], 'yo')
+ self.assertEqual(result['subpath'], ['a', 'b','c'])
+ self.assertEqual(result['traversed'], None)
+ self.assertEqual(result['virtual_root'], model)
+ self.assertEqual(result['virtual_root_path'], None)
def test_with_path_info(self):
model = DummyContext()
@@ -261,12 +261,12 @@ class RoutesModelTraverserTests(unittest.TestCase):
environ = {'wsgiorg.routing_args': routing_args, 'bfg.route': route,
'PATH_INFO':'/a/b/foo/bar', 'SCRIPT_NAME':''}
result = traverser(environ)
- self.assertEqual(result[0], model)
- self.assertEqual(result[1], 'yo')
- self.assertEqual(result[2], [])
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], model)
- self.assertEqual(result[5], None)
+ self.assertEqual(result['context'], model)
+ self.assertEqual(result['view_name'], 'yo')
+ self.assertEqual(result['subpath'], [])
+ self.assertEqual(result['traversed'], None)
+ self.assertEqual(result['virtual_root'], model)
+ self.assertEqual(result['virtual_root_path'], None)
self.assertEqual(environ['PATH_INFO'], '/foo/bar')
self.assertEqual(environ['SCRIPT_NAME'], '/a/b')
@@ -277,7 +277,7 @@ class RoutesModelTraverserTests(unittest.TestCase):
route = DummyRoute('yo')
environ = {'wsgiorg.routing_args': routing_args, 'bfg.route':route,
'PATH_INFO':'/a/b//foo/bar', 'SCRIPT_NAME':''}
- result = traverser(environ)
+ traverser(environ)
self.assertEqual(environ['PATH_INFO'], '/foo/bar')
self.assertEqual(environ['SCRIPT_NAME'], '/a/b')
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py
index 49017a93b..f21f27a39 100644
--- a/repoze/bfg/traversal.py
+++ b/repoze/bfg/traversal.py
@@ -2,7 +2,6 @@ import re
import urllib
from zope.component import getMultiAdapter
-from zope.component import queryUtility
from zope.deprecation import deprecated
@@ -17,7 +16,6 @@ from repoze.bfg.interfaces import IContextURL
from repoze.bfg.interfaces import ITraverser
from repoze.bfg.interfaces import ITraverserFactory
from repoze.bfg.interfaces import VH_ROOT_KEY
-from repoze.bfg.interfaces import ILogger
def find_root(model):
""" Find the root node in the graph to which ``model``
@@ -74,39 +72,12 @@ def find_model(model, path):
object representing a model name). Model path tuples generated by
``model_path_tuple`` can always be resolved by ``find_model``.
"""
-
- if hasattr(path, '__iter__'): # it's a tuple or some other iterable
- # the traverser factory expects PATH_INFO to be a string, not
- # unicode and it expects path segments to be utf-8 and
- # urlencoded (it's the same traverser which accepts PATH_INFO
- # from user agents; user agents always send strings).
- path = [quote_path_segment(name) for name in path]
- if path:
- path = '/'.join(path) or '/'
- else:
- path = ''
-
- if path and path[0] == '/':
- model = find_root(model)
-
- traverser = ITraverserFactory(model)
- val = traverser({'PATH_INFO':path})
- try:
- ob, name, _, _, _, _ = val
- except ValueError:
- # b/w compat for three-value-returning ITraverser (prior to 0.7.1)
- logger = queryUtility(ILogger, 'repoze.bfg.debug')
- logger and logger.warn(
- '%s is an pre-0.7.1-style ITraverser returning only '
- '3 arguments; please update it to the new '
- '6-argument-returning interface for improved '
- 'functionality. See the repoze.bfg.interfaces module '
- 'for the new ITraverser interface '
- 'definition' % traverser)
- ob, name, _ = val
- if name:
- raise KeyError('%r has no subelement %s' % (ob, name))
- return ob
+ D = traverse(model, path)
+ view_name = D['view_name']
+ context = D['context']
+ if view_name:
+ raise KeyError('%r has no subelement %s' % (context, view_name))
+ return context
def find_interface(model, interface):
"""
@@ -162,6 +133,162 @@ def model_path(model, *elements):
path = _model_path_list(model, *elements)
return path and '/'.join([quote_path_segment(x) for x in path]) or '/'
+def traverse(model, path):
+ """Given a model object as ``model` and a string or tuple
+ representing a path as ``path`` (such as the return value of
+ ``repoze.bfg.traversal.model_path`` or
+ ``repoze.bfg.traversal.model_path_tuple`` or the value of
+ ``request.environ['PATH_INFO']``), return a dictionary with the
+ keys ``context``, ``root``, ``view_name``, ``subpath``,
+ ``traversed``, ``virtual_root``, and ``virtual_root_path``.
+
+ A definition of each value in the returned dictionary:
+
+ - ``context``: The context (a 'model' object) found via traversal
+ or url dispatch. If the ``path`` passed in is the empty string,
+ the value of the ``model`` argument passed to this function is
+ returned.
+
+ - ``root``: The root model object found via traversal or url
+ dispatch. If the ``model`` passed in was found via url
+ dispatch, the value of the ``model`` argument passed to this
+ function is returned.
+
+ - ``view_name``: The 'view name' found during traversal or url
+ dispatch; if the ``model`` was found via traversal, this is
+ usually a representation of the path segment which directly
+ follows the path to the ``context`` in the ``path``. The
+ ``view_name`` will be a Unicode object or the empty string. The
+ ``view_name`` will be the empty string if there is no element
+ which follows the ``context`` path. An example: if the path
+ passed is ``/foo/bar``, and a context object is found at
+ ``/foo`` (but not at ``/foo/bar``), the 'view name' will be
+ ``u'bar'``. If the ``model`` was found via urldispatch, the
+ view_name will be the name the route found was registered with.
+
+ - ``subpath``: For a ``model`` found via traversal, this is a
+ sequence of path segments found in the ``path`` that follow the
+ ``view_name`` (if any). Each of these items is a Unicode
+ object. If no path segments follow the ``view_name``, the
+ subpath will be the empty list. An example: if the path passed
+ is ``/foo/bar/baz/buz``, and a context object is found at
+ ``/foo`` (but not ``/foo/bar``), the 'view name' will be
+ ``u'bar'`` and the subpath will be ``[u'baz', u'buz']``. For a
+ ``model`` found via url dispatch, the subpath will be a sequence
+ of values discerned from ``*subpath`` in the route pattern
+ matched or the empty sequence.
+
+ - ``traversed``: The sequence of path elements traversed to find
+ the ``context`` object. Each of these items is a Unicode
+ object. If no path segments were traversed to find the
+ ``context`` object (e.g. if the ``path`` provided is the empty
+ string), the ``traversed`` value will be the empty list. If the
+ ``model`` is a model found via urldispatch, traversed will be None.
+
+ - ``virtual_root``: A model object representing the 'virtual' root
+ of the object graph being traversed. See
+ :ref:`vhosting_chapter` for a definition of the virtual root
+ object. If no virtual hosting is in effect or if the ``model``
+ passed in was found via URL dispatch, the ``virtual_root`` will
+ always be the physical root of the object graph (the object at
+ which traversal begins).
+
+ - ``virtual_root_path`` -- If traversal was used to find the
+ ``model``, this will be the sequence of path elements traversed
+ to find the ``virtual_root`` object. Each of these items is a
+ Unicode object. If no path segments were traversed to find the
+ ``virtual_root`` object (e.g. if virtual hosting is not in
+ effect), the ``traversed`` value will be the empty list. If url
+ dispatch was used to find the ``model``, this will be None.
+
+ If the path cannot be resolved, a KeyError will be raised.
+
+ Rules for passing a *string* as the ``path`` argument: if the
+ first character in the path string is the with the ``/``
+ character, the path will considered absolute and the graph
+ traversal will start at the root object. If the first character
+ of the path string is *not* the ``/`` character, the path is
+ considered relative and graph traversal will begin at the model
+ object supplied to the function as the ``model`` argument. If an
+ empty string is passed as ``path``, the ``model`` passed in will
+ be returned. Model path strings must be escaped in the following
+ manner: each Unicode path segment must be encoded as UTF-8 and as
+ each path segment must escaped via Python's ``urllib.quote``. For
+ example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or
+ ``to%20the/La%20Pe%C3%B1a`` (relative). The ``model_path``
+ function generates strings which follow these rules (albeit only
+ absolute ones).
+
+ Rules for passing a *tuple* as the ``path`` argument: if the first
+ element in the path tuple is the empty string (for example ``('',
+ 'a', 'b', 'c')``, the path is considered absolute and the graph
+ traversal will start at the graph root object. If the first
+ element in the path tuple is not the empty string (for example
+ ``('a', 'b', 'c')``), the path is considered relative and graph
+ traversal will begin at the model object supplied to the function
+ as the ``model`` argument. If an empty sequence is passed as
+ ``path``, the ``model`` passed in itself will be returned. No
+ URL-quoting or UTF-8-encoding of individual path segments within
+ the tuple is required (each segment may be any string or unicode
+ object representing a model name). Model path tuples generated by
+ ``model_path_tuple`` can always be resolved by ``find_model``.
+
+ Explanation of the conversion of ``path`` segment values to
+ Unicode during traversal: Each segment is URL-unquoted, and
+ decoded into Unicode. Each segment is assumed to be encoded using
+ the UTF-8 encoding (or a subset, such as ASCII); a TypeError is
+ raised if a segment cannot be decoded. If a segment name is empty
+ or if it is ``.``, it is ignored. If a segment name is ``..``,
+ the previous segment is deleted, and the ``..`` is ignored. As a
+ result of this process, the return values ``view_name``, each
+ element in the ``subpath``, each element in ``traversed``, and
+ each element in the ``virtual_root_path`` will be Unicode as
+ opposed to a string, and will be URL-decoded.
+ """
+ if hasattr(path, '__iter__'): # it's a tuple or some other iterable
+ # the traverser factory expects PATH_INFO to be a string, not
+ # unicode and it expects path segments to be utf-8 and
+ # urlencoded (it's the same traverser which accepts PATH_INFO
+ # from user agents; user agents always send strings).
+ path = [quote_path_segment(name) for name in path]
+ if path:
+ path = '/'.join(path) or '/'
+ else:
+ path = ''
+
+ if path and path[0] == '/':
+ model = find_root(model)
+
+ return _traverse(model, {'PATH_INFO':path})
+
+def _traverse(model, environ):
+ traverser = ITraverserFactory(model)
+ result = traverser(environ)
+ deprecation_warning = None
+ if not isinstance(result, dict):
+ try:
+ # b/w compat for 6-arg returning ITraversers (0.7.1 til 0.8a7)
+ ctx, view_name, subpath, traversed, vroot, vroot_path = result
+ except ValueError:
+ # b/w compat for 3-arg returning ITraversers (0.7.0-style
+ # and below)
+ ctx, view_name, subpath = result
+ traversed = None
+ vroot = None
+ vroot_path = None
+ deprecation_warning = (
+ '%r is an pre-0.8-style ITraverser returning a %s-argument tuple; '
+ 'please update it to the new ITraverser interface which returns '
+ 'a dictionary for improved functionality. See the '
+ '"repoze.bfg.interfaces" module for the new ITraverser interface '
+ 'definition.' % (traverser.__class__, len(result)))
+
+ result = {'context':ctx, 'view_name':view_name, 'subpath':subpath,
+ 'traversed':traversed, 'virtual_root':vroot,
+ 'virtual_root_path':vroot_path, 'root':None,
+ '_deprecation_warning':deprecation_warning}
+ return result
+
def model_path_tuple(model, *elements):
"""
Return a tuple representing the absolute physical path of the
@@ -394,22 +521,30 @@ class ModelGraphTraverser(object):
for segment in path:
if segment[:2] =='@@':
- return ob, segment[2:], path[i:], traversed, vroot, vroot_path
+ return dict(context=ob, view_name=segment[2:], subpath=path[i:],
+ traversed=traversed, virtual_root=vroot,
+ virtual_root_path=vroot_path, root=self.root)
try:
getitem = ob.__getitem__
except AttributeError:
- return ob, segment, path[i:], traversed, vroot, vroot_path
+ return dict(context=ob, view_name=segment, subpath=path[i:],
+ traversed=traversed, virtual_root=vroot,
+ virtual_root_path=vroot_path, root=self.root)
try:
next = getitem(segment)
except KeyError:
- return ob, segment, path[i:], traversed, vroot, vroot_path
+ return dict(context=ob, view_name=segment, subpath=path[i:],
+ traversed=traversed, virtual_root=vroot,
+ virtual_root_path=vroot_path, root=self.root)
if vroot_idx == i-1:
vroot = ob
traversed.append(segment)
ob = next
i += 1
- return ob, '', [], traversed, vroot, vroot_path
+ return dict(context=ob, view_name=u'', subpath=[], traversed=traversed,
+ virtual_root=vroot, virtual_root_path=vroot_path,
+ root=self.root)
class TraversalContextURL(object):
""" The IContextURL adapter used to generate URLs for a context
diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py
index 4c3bc2ed3..3e60c0ba5 100644
--- a/repoze/bfg/urldispatch.py
+++ b/repoze/bfg/urldispatch.py
@@ -154,7 +154,9 @@ class RoutesModelTraverser(object):
if environ['SCRIPT_NAME'].endswith('/'):
environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'][:-1]
- return self.context, route.name, subpath, None, self.context, None
+ return dict(context=self.context, view_name=route.name,
+ subpath=subpath, traversed=None, virtual_root=self.context,
+ virtual_root_path=None, root=self.context)
class RoutesContextURL(object):
""" The IContextURL adapter used to generate URLs for a context