summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-10-30 19:38:41 +0000
committerChris McDonough <chrism@agendaless.com>2009-10-30 19:38:41 +0000
commitacc7765a037983907d3275312396ee10b6b9ad2e (patch)
treedc91f45c5b638f6c86ec9c74b627e37251707d7c
parent11644e705834ff65cb8963333855a1db6272ae1e (diff)
downloadpyramid-acc7765a037983907d3275312396ee10b6b9ad2e.tar.gz
pyramid-acc7765a037983907d3275312396ee10b6b9ad2e.tar.bz2
pyramid-acc7765a037983907d3275312396ee10b6b9ad2e.zip
- The ``__call__`` of a plugin "traverser" implementation (registered
as an adapter for ``ITraverser`` or ``ITraverserFactory``) will now receive a *request* as the single argument to its ``__call__`` method. In previous versions it was passed a WSGI ``environ`` object. The request object passed to the factory implements dictionary-like methods in such a way that existing traverser code which expects to be passed an environ will continue to work. - Fix docs.
-rw-r--r--CHANGES.txt8
-rw-r--r--docs/narr/hooks.rst12
-rw-r--r--docs/narr/hybrid.rst2
-rw-r--r--docs/narr/router.rst42
-rw-r--r--docs/narr/security.rst2
-rw-r--r--docs/narr/traversal.rst21
-rw-r--r--docs/narr/urldispatch.rst12
-rw-r--r--repoze/bfg/router.py6
-rw-r--r--repoze/bfg/tests/test_router.py2
-rw-r--r--repoze/bfg/traversal.py19
10 files changed, 79 insertions, 47 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index ca6c501fb..b664ab092 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -22,6 +22,14 @@ Features
factory code which expects to be passed an environ will continue to
work.
+- The ``__call__`` of a plugin "traverser" implementation (registered
+ as an adapter for ``ITraverser`` or ``ITraverserFactory``) will now
+ receive a *request* as the single argument to its ``__call__``
+ method. In previous versions it was passed a WSGI ``environ``
+ object. The request object passed to the factory implements
+ dictionary-like methods in such a way that existing traverser code
+ which expects to be passed an environ will continue to work.
+
Internal
--------
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index d2020af0d..0fea7c64e 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -231,7 +231,7 @@ a class that implements the following interface:
def __init__(self, root):
""" Accept the root object returned from the root factory """
- def __call__(self, environ):
+ def __call__(self, request):
""" Return a dictionary with (at least) the keys ``root``,
``context``, ``view_name``, ``subpath``, ``traversed``,
``virtual_root``, and ``virtual_root_path``. These values are
@@ -255,6 +255,16 @@ a class that implements the following interface:
as attributes of the ``request`` object.
"""
+.. warning:: In :mod:`repoze.bfg.` 1.0 and previous versions, the
+ traverser ``__call__`` method accepted a WSGI *environment*
+ dictionary rather than a :term:`request` object. The request
+ object passed to the traverser implements a dictionary-like API
+ which mutates and queries the environment, as a backwards
+ compatibility shim, in order to allow older code to work.
+ However, for maximum forward compatibility, traverser code
+ targeting :mod:`repoze.bfg` 1.1 and higher should expect a
+ request object directly.
+
More than one traversal algorithm can be active at the same time. For
instance, if your :term:`root factory` returns more than one type of
object conditionally, you could claim that an alternate traverser is
diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst
index 34f606a97..4ff5df493 100644
--- a/docs/narr/hybrid.rst
+++ b/docs/narr/hybrid.rst
@@ -138,7 +138,7 @@ looks like so:
root = Traversable(
{'a':Traversable({'b':Traversable({'c':Traversable({})})})})
- def root_factory(environ):
+ def root_factory(request):
return root
We've defined a bogus graph here that can be traversed, and a
diff --git a/docs/narr/router.rst b/docs/narr/router.rst
index 55df2936c..7382c07ae 100644
--- a/docs/narr/router.rst
+++ b/docs/narr/router.rst
@@ -21,18 +21,22 @@ processing?
the WSGI environment to the ``__call__`` method of the
:mod:`repoze.bfg` :term:`router` object.
+#. A :term:`request` object is created based on the WSGI environment.
+
#. To service :term:`url dispatch`, the :mod:`repoze.bfg`
:term:`router` calls a :term:`URL dispatch` "root factory wrapper"
callable, which acts as a :term:`root factory`. The job of the
- mapper is to examine the ``PATH_INFO`` and other various keys in
- the environment to determine whether any user-defined :term:`route`
- matches the current WSGI environment. The :term:`router` passes
- the WSGI environment as an argument to the mapper.
+ mapper is to examine the ``PATH_INFO`` implied by the request to
+ determine whether any user-defined :term:`route` matches the
+ current WSGI environment. The :term:`router` passes the request as
+ an argument to the mapper.
#. If any route matches, the WSGI environment is mutated; a
``bfg.routes.route`` key and a ``bfg.routes.matchdict`` are added
- to the WSGI environment. If a route *doesn't* match, neither of
- these keys is added to the WSGI environment.
+ to the WSGI environment, and an attribute named ``matchdict`` is
+ added to the request. If a route *doesn't* match, neither of these
+ keys is added to the WSGI environment and the request object is not
+ mutated.
#. Regardless of whether any route matched or not, the :term:`URL
dispatch` mapper returns a root object. If a particular
@@ -44,27 +48,17 @@ processing?
``make_app`` is ``None``, a default root factory is used to
generate a root.
-#. A :term:`WebOb` :term:`request` is generated using the WSGI
- environment. The request type (its class and any :term:`interface`
- attached to it) is dependent upon a combination of the
- ``REQUEST_METHOD`` of the WSGI environment as well as any
- :term:`route` match. For example, a very particular kind of
- request object is generated when the request has a
- ``REQUEST_METHOD`` of ``POST`` and a :term:`route` named "home"
- matches. We use the request type to determine exactly which
- :term:`view` to call later.
-
#. A ``NewRequest`` :term:`event` is sent to any subscribers.
#. The :mod:`repoze.bfg` router calls a "traverser" function with the
- root object and the WSGI environment. The traverser function
- attempts to traverse the root object (using any existing
- ``__getitem__`` on the root object and subobjects) to find a
- :term:`context`. If the root object has no ``__getitem__`` method,
- the root itself is assumed to be the context. The exact traversal
- algorithm is described in :ref:`traversal_chapter`. The traverser
- function returns a dictionary, which contains a :term:`context` and
- a :term:`view name` as well as other ancillary information.
+ root object and the request. The traverser function attempts to
+ traverse the root object (using any existing ``__getitem__`` on the
+ root object and subobjects) to find a :term:`context`. If the root
+ object has no ``__getitem__`` method, the root itself is assumed to
+ be the context. The exact traversal algorithm is described in
+ :ref:`traversal_chapter`. The traverser function returns a
+ dictionary, which contains a :term:`context` and a :term:`view
+ name` as well as other ancillary information.
#. The request is decorated with various names returned from the
traverser (such as ``context``, ``view_name``, and so forth), so
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 90ead339c..773ab3a4f 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -522,7 +522,7 @@ An example of its usage, with all attributes fully expanded:
The ``identifier_name`` controls the name used to look up the
:term:`repoze.who` "identifier" plugin within
-``environ['repoze.who.plugins']`` which is used by this policy to
+``request.environ['repoze.who.plugins']`` which is used by this policy to
"remember" and "forget" credentials. It defaults to ``auth_tkt``.
The ``callback`` is a Python dotted name to a function passed the
diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst
index 340f8c277..5ed3db7cb 100644
--- a/docs/narr/traversal.rst
+++ b/docs/narr/traversal.rst
@@ -23,11 +23,10 @@ Users interact with your :mod:`repoze.bfg` -based application via a
*router*, which is just a fancy :term:`WSGI` application. At system
startup time, the router is configured with a callback known as a
:term:`root factory`, supplied by the application developer. The root
-factory is passed the WSGI "environment" (a dictionary) and it is
-expected to return an object which represents the root of the model
-graph. All :term:`traversal` will begin at this root object. The
-root object is usually a *mapping* object (such as a Python
-dictionary).
+factory is passed a :term:`request` object and it is expected to
+return an object which represents the root of the model graph. All
+:term:`traversal` will begin at this root object. The root object is
+usually a *mapping* object (such as a Python dictionary).
.. note:: If a :term:`root factory` is passed to the :mod:`repoze.bfg`
"make_app" function as the value ``None``, a default root factory
@@ -43,6 +42,14 @@ dictionary).
matched, it is also possible to do traversal *after* a route has
been matched. See :ref:`hybrid_chapter` for more information.
+.. warning:: In BFG 1.0 and prior versions, the root factory was
+ passed a term WSGI *environment* object (a dictionary) while in
+ BFG 1.1+ it is passed a request object. For backwards
+ compatibility purposes, the request object passed to the root
+ factory has a dictionary-like interface that emulates the WSGI
+ environment, so code expecting the argument to be a dictionary
+ will continue to work.
+
Items contained within the object graph are analogous to the concept
of :term:`model` objects used by many other frameworks (and
:mod:`repoze.bfg` refers to them as models, as well). They are
@@ -93,10 +100,10 @@ code to execute:
#. The router creates a :term:`WebOb` request object based on the
WSGI environment.
-#. The :term:`root factory` is called with the WSGI environment. It
+#. The :term:`root factory` is called with the :term:`request`. It
returns a :term:`root` object.
-#. The router uses the WSGI environment's ``PATH_INFO`` variable to
+#. The router uses the request's ``PATH_INFO`` information to
determine the path segments to traverse. The leading slash is
stripped off ``PATH_INFO``, and the remaining path segments are
split on the slash character to form a traversal sequence, so a
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 7a2a184f7..26d4b4a93 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -472,7 +472,7 @@ dispatch will by default be an instance of the object returned by the
default :term:`root factory`. You can override this behavior by
passing in a ``factory`` argument to the ZCML directive for a
particular route. The ``factory`` should be a callable that accepts a
-WSGI environment and returns an instance of a class that will be the
+:term:`request` and returns an instance of a class that will be the
context used by the view.
An example of using a route with a factory:
@@ -489,7 +489,7 @@ An example of using a route with a factory:
The above route will manufacture an ``Idea`` model as a context,
assuming that ``mypackage.models.Idea`` resolves to a class that
-accepts a WSGI environment in its ``__init__``.
+accepts a request in its ``__init__``.
.. note:: Values prefixed with a period (``.``) for the ``factory``
and ``view`` attributes of a ``route`` (such as ``.models.Idea``
@@ -536,8 +536,8 @@ The ``.models`` module referred to above might look like so:
:linenos:
class Article(object):
- def __init__(self, environ):
- self.__dict__.update(environ['bfg.routes.matchdict'])
+ def __init__(self, request):
+ self.__dict__.update(request.matchdict)
def is_root(self):
return self.article == 'root'
@@ -739,8 +739,8 @@ Such a ``factory`` might look like so:
:linenos:
class Article(object):
- def __init__(self, environ):
- matchdict = environ['bfg.routes.matchdict']
+ def __init__(self, request):
+ matchdict = request.matchdict
article = matchdict.get('article', None)
if article == '1':
self.__acl__ = [ (Allow, 'editor', 'view') ]
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index 3916d6627..c48a96664 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -71,7 +71,7 @@ class Router(object):
traverser = registry.queryAdapter(root, ITraverser)
if traverser is None:
traverser = ModelGraphTraverser(root)
- tdict = traverser(environ)
+ tdict = traverser(request)
context, view_name, subpath, traversed, vroot, vroot_path = (
tdict['context'], tdict['view_name'], tdict['subpath'],
tdict['traversed'], tdict['virtual_root'],
@@ -136,8 +136,8 @@ def make_app(root_factory, package=None, filename='configure.zcml',
""" Return a Router object, representing a fully configured
``repoze.bfg`` WSGI application.
- ``root_factory`` must be a callable that accepts a WSGI
- environment and returns a traversal root object. The traversal
+ ``root_factory`` must be a callable that accepts a :term:`request`
+ object and which returns a traversal root object. The traversal
root returned by the root factory is the *default* traversal root;
it can be overridden on a per-view basis. ``root_factory`` may be
``None``, in which case a 'default default' traversal root is
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index d2fbf4b2d..ac77508ef 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -46,7 +46,7 @@ class TestRouter(unittest.TestCase):
def __init__(self, root):
self.root = root
- def __call__(self, path):
+ def __call__(self, request):
values = {'root':self.root,
'context':context,
'view_name':view_name,
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py
index 43adcdebb..d48627e7e 100644
--- a/repoze/bfg/traversal.py
+++ b/repoze/bfg/traversal.py
@@ -268,12 +268,12 @@ def traverse(model, path):
if path and path[0] == '/':
model = find_root(model)
- environ = {'PATH_INFO':path}
+ request = FakeRequest({'PATH_INFO':path})
traverser = queryAdapter(model, ITraverser)
if traverser is None:
traverser = ModelGraphTraverser(model)
- return traverser(environ)
+ return traverser(request)
def model_path_tuple(model, *elements):
"""
@@ -474,7 +474,15 @@ class ModelGraphTraverser(object):
def __init__(self, root):
self.root = root
- def __call__(self, environ):
+ def __call__(self, request):
+ try:
+ environ = request.environ
+ except AttributeError:
+ # In BFG 1.0 and before, this API expected an environ
+ # rather than a request; some bit of code may still be
+ # passing us an environ. If so, deal.
+ environ = request
+
if 'bfg.routes.matchdict' in environ:
matchdict = environ['bfg.routes.matchdict']
@@ -623,3 +631,8 @@ class TraversalContextURL(object):
def _join_path_tuple(tuple):
return tuple and '/'.join([quote_path_segment(x) for x in tuple]) or '/'
+class FakeRequest(dict):
+ def __init__(self, environ):
+ self.update(environ)
+ self.environ = self # XXX circref?
+