diff options
| author | Chris McDonough <chrism@agendaless.com> | 2010-01-16 08:51:15 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2010-01-16 08:51:15 +0000 |
| commit | 41cc8f504cd82d9cfe80c0c2ee18a17cbaaf3c18 (patch) | |
| tree | cbcd6a517777f2d2e0e2e7b2c54be4220edfe37e | |
| parent | cb4e28329325106507ca411b2e2a1c118fa8449a (diff) | |
| download | pyramid-41cc8f504cd82d9cfe80c0c2ee18a17cbaaf3c18.tar.gz pyramid-41cc8f504cd82d9cfe80c0c2ee18a17cbaaf3c18.tar.bz2 pyramid-41cc8f504cd82d9cfe80c0c2ee18a17cbaaf3c18.zip | |
Traversal mini-overhaul.
| -rw-r--r-- | docs/narr/traversal.rst | 246 | ||||
| -rw-r--r-- | docs/narr/views.rst | 3 | ||||
| -rw-r--r-- | docs/narr/webob.rst | 2 |
3 files changed, 142 insertions, 109 deletions
diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 08375d599..14d9fe12b 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -10,8 +10,8 @@ graph*, starting from a :term:`root` object, using a :term:`request` object as a source of path information. In this chapter, we'll provide a high-level overview of traversal, -explain the concept of an *object graph*, and show how traversal might -be used within an application. +we'll explain the concept of an *object graph*, and we'll show how +traversal might be used within an application. .. index:: pair: traversal; high-level overview @@ -47,10 +47,10 @@ the traversal process. The combination of the :term:`context` object and the :term:`view name` found via traversal is used later in the same request by a -separate :mod:`repoze.bfg` subsystem -- the "view lookup" subsystem -- -to find a :term:`view callable` later within the same request. How -:mod:`repoze.bfg` performs view lookup is explained within the -:ref:`views_chapter` chapter. +separate :mod:`repoze.bfg` subsystem -- the :term:`view lookup` +subsystem -- to find a :term:`view callable` later within the same +request. How :mod:`repoze.bfg` performs view lookup is explained +within the :ref:`views_chapter` chapter. .. index:: single: object graph @@ -81,6 +81,9 @@ Here's an example of using this root factory within startup configuration, by passing it to an instance of a :term:`Configurator` named ``config``: +.. code-block:: python + :linenos: + config = Configurator(root_factory=Root) Making a declaration like this at startup means that your @@ -94,8 +97,7 @@ another persistence mechanism. A root factory is passed a :term:`request` object and it is expected to return an object which represents the root of the object graph. All :term:`traversal` will begin at this root object. The root object -is usually a *mapping* object (such as a Python dictionary, or at -least a class which has many of the same methods as a dictionary). +is often an instance of a class which has a ``__getitem__`` method. .. warning:: In :mod:`repoze.bfg` 1.0 and prior versions, the root factory was passed a term WSGI *environment* object (a dictionary) @@ -128,7 +130,8 @@ root factory is used. behavior or state. Using :term:`traversal` against an application that uses the object graph supplied by the default root object is not very interesting, because the default root object has no - children. + children. Its availability is more useful when you're developing + an application using :term:`URL dispatch`. Items contained within the object graph are sometimes analogous to the concept of :term:`model` objects used by many other frameworks (and @@ -136,12 +139,32 @@ concept of :term:`model` objects used by many other frameworks (and They are typically instances of Python classes. .. index:: - single: traversal behavior + single: traversal; algorithm .. _traversal_behavior: -:mod:`repoze.bfg` Traversal Behavior -------------------------------------- +The :mod:`repoze.bfg` Traversal Algorithm +----------------------------------------- + +This section will attempt to explain the :mod:`repoze.bfg` traversal +algorithm. We'll provide an an analogy, a diagram of how the +traversal algorithm works, and some example traversal scenarios that +might aid in understanding how the traversal algorithm operates +against a specific object graph. + +The :ref:`views_chapter` chapter discusses :term:`view lookup` in +detail, and it is the canonical source for information about views. +Technically, :term:`traversal` is a :mod:`repoze.bfg` subsystem that +is separated from traversal entirely. However, we'll describe the +fundamental behavior of view lookup in the examples in the next few +sections to give you an idea of how traversal and view lookup +cooperate, because they are always used cooperatively. + +.. index:: + single: traversal analogy + +An Analogy +~~~~~~~~~~ We need to use an analogy to clarify how :mod:`repoze.bfg` traversal works against an arbitrary object graph. @@ -186,12 +209,12 @@ The contents of ``myfile`` are now printed on the user's behalf. uses :term:`traversal` against an object graph. In this analogy, we can map the ``cat`` program to the :mod:`repoze.bfg` concept of a :term:`view callable`: it is a program that can be run against some -:term:`context`. The file being operated on in this analogy is the -:term:`context` object; the context is the "last node found" in a -traversal. The directory structure is the object graph being -traversed. The act of progressively changing directories to find the -file as well as the handling of a ``cd`` error as a stop condition is -analogous to :term:`traversal`. +:term:`context` as the result of :term:`view lookup`. The file being +operated on in this analogy is the :term:`context` object; the context +is the "last node found" in a traversal. The directory structure is +the object graph being traversed. The act of progressively changing +directories to find the file as well as the handling of a ``cd`` error +as a stop condition is analogous to :term:`traversal`. The object graph is traversed, beginning at a root object, represented by the root URL (``/``); if there are further path segments in the @@ -231,18 +254,22 @@ model instance in your object graph or when the path segments implied by the URL "run out". The object that traversal "stops on" becomes the :term:`context`. +.. index:: + pair: traversal; unicode + pair: traversal; algorithm + .. _how_bfg_traverses: -How :mod:`repoze.bfg` Processes a Request Using Traversal ---------------------------------------------------------- +The Algorithm +~~~~~~~~~~~~~ -When a user requests a page from your :mod:`repoze.bfg` -powered +When a user requests a page from your :mod:`traversal` -powered application, the system uses this algorithm to determine which Python code to execute: #. The request for the page is presented to the :mod:`repoze.bfg` :term:`router` in terms of a standard :term:`WSGI` request, which - is represented by a WSGI environment and a ``start_response`` + is represented by a WSGI environment and a WSGI ``start_response`` callable. #. The router creates a :term:`request` object based on the WSGI @@ -258,8 +285,8 @@ code to execute: request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the traversal sequence ``[u'a', u'b', u'c']``. Note that each of the path segments in the sequence is converted to Unicode using the - UTF-8 decoding (if the decoding fails, a :exc:`TypeError` is - raised). + UTF-8 decoding; if the decoding fails, a :exc:`TypeError` is + raised. #. :term:`Traversal` begins at the root object returned by the root factory. For the traversal sequence ``[u'a', u'b', u'c']``, the @@ -300,34 +327,50 @@ code to execute: the context is "object ``a``", the view name is ``b`` and the subpath is ``('c',)``. -#. If a :term:`authorization policy` is configured, the router - performs a permission lookup. If a permission declaration is - found for the view name and context implied by the current - request, the :term:`authorization policy` is consulted to see if - the "current user" (as determined by the the active - :term:`authentication policy`) can perform the action. If he can, - processing continues. If he cannot, the :term:`forbidden view` is - called (see also :ref:`changing_the_forbidden_view`). - -#. Armed with the context, the view name, and the subpath, the router - performs a view lookup. It attempts to look up a view from the - :mod:`repoze.bfg` :term:`application registry` using the - :term:`view name`, the :term:`context`, and the :term:`request`. - If a view function is found, it is called with the context and the - request. It returns a response, which is fed back upstream. If a - view is not found, the :term:`not found view` is called (see - :ref:`changing_the_notfound_view`). - -In either case, the result is returned upstream via the :term:`WSGI` -protocol. +Once :term:`context` and :term:`view name` and associated attributes +such as the :term:`subpath` are located, the job of :term:`traversal` +is finished. It passes the back the information it obtained to its +caller, the :mod:`repoze.bfg` :term:`Router`, which subsequently +invokes :term:`view lookup` with the context and view name +information. + +Note well that the traversal machinery by default attempts to first +URL-unquote and then Unicode-decode each path element in ``PATH_INFO`` +from its natural byte string (``str`` type) representation. URL +unquoting is performed using the Python standard library +``urllib.unquote`` function. Conversion from a URL-decoded string +into Unicode is attempted using the UTF-8 encoding. If any +URL-unquoted path segment in ``PATH_INFO`` is not decodeable using the +UTF-8 decoding, a :exc:`TypeError` is raised. A segment will be fully +URL-unquoted and UTF8-decoded before it is passed it to the +``__getitem__`` of any model object during traversal. + +The standard traversal algorithm exposes two special cases: + +- You will often end up with a :term:`view name` that is the empty + string as the result of a particular traversal. This indicates that + the view lookup machinery should look up the :term:`default view`. + The default view is a view that is registered with no name or a view + which is registered with a name that equals the empty string. + +- If any path segment element begins with the special characters + ``@@`` (think of them as goggles), the value of that segment minus + the goggle characters is considered the :term:`view name` + immediately and traversal stops there. This allows you to address + views that may have the same names as model instance names in the + graph unambiguously. .. image:: modelgraphtraverser.png .. index:: pair: traversal; example -A Traversal Example -------------------- +Traversal Examples +~~~~~~~~~~~~~~~~~~ + +No one can be expected to understand the traversal algorithm by +analogy and description alone, so let's examine some traversal +scenarios that use concrete URLs and object graph compositions. Let's pretend the user asks for ``http://example.com/foo/bar/baz/biz/buz.txt``. Let's pretend that the @@ -343,15 +386,14 @@ traversing the following graph:: Here's what happens: -- :mod:`repoze.bfg` traverses the root, and attempts to find "foo", +- :mod:`traversal` traverses the root, and attempts to find "foo", which it finds. -- :mod:`repoze.bfg` traverses "foo", and attempts to find "bar", which +- :mod:`traversal` traverses "foo", and attempts to find "bar", which it finds. -- :mod:`repoze.bfg` traverses bar, and attempts to find "baz", which - it does not find ("bar" raises a :exc:`KeyError` when asked for - "baz"). +- :mod:`traversal` traverses bar, and attempts to find "baz", which it + does not find ("bar" raises a :exc:`KeyError` when asked for "baz"). The fact that it does not find "baz" at this point does not signify an error condition. It signifies that: @@ -363,20 +405,21 @@ error condition. It signifies that: - the :term:`subpath` is ``('biz', 'buz.txt')`` -Because it's the "context", :mod:`repoze.bfg` examines "bar" to find -out what "type" it is. Let's say it finds that the context is an -``Bar`` type (because "bar" happens to be an instance of the class -``Bar``). +At this point, traversal has ended, and :term:`view lookup` begins. -Using the :term:`view name` (``baz``) and the type, it asks the -:term:`application registry` this question: +Because it's the "context", the view lookup machinery examines "bar" +to find out what "type" it is. Let's say it finds that the context is +an ``Bar`` type (because "bar" happens to be an instance of the class +``Bar``). Using the :term:`view name` (``baz``) and the type, view +lookup asks the :term:`application registry` this question: - Please find me a :term:`view callable` registered using a :term:`view configuration` with the name "baz" that can be used for the class ``Bar``. -Let's say it finds no matching view type. It then returns the result -of the :term:`not found view`. The request ends. +Let's say that view lookup finds no matching view type. In this +circumstance, the :mod:`repoze.bfg` :term:`router` returns the result +of the :term:`not found view` and the request ends. However, for this graph:: @@ -392,16 +435,16 @@ However, for this graph:: The user asks for ``http://example.com/foo/bar/baz/biz/buz.txt`` -- :mod:`repoze.bfg` traverses "foo", and attempts to find "bar", which +- :mod:`traversal` traverses "foo", and attempts to find "bar", which it finds. -- :mod:`repoze.bfg` traverses "bar", and attempts to find "baz", which +- :mod:`traversal` traverses "bar", and attempts to find "baz", which it finds. -- :mod:`repoze.bfg` traverses "baz", and attempts to find "biz", which +- :mod:`traversal` traverses "baz", and attempts to find "biz", which it finds. -- :mod:`repoze.bfg` traverses "biz", and attempts to find "buz.txt" +- :mod:`traversal` traverses "biz", and attempts to find "buz.txt" which it does not find. The fact that it does not find "buz.txt" at this point does not @@ -414,61 +457,46 @@ signify an error condition. It signifies that: - the :term:`subpath` is an empty sequence ( ``()`` ). -Because it's the "context", :mod:`repoze.bfg` examines "biz" to find -out what "type" it is. Let's say it finds that the context is a -``Biz`` type (because "biz" is an instance of the Python class -``Biz``). +At this point, traversal has ended, and :term:`view lookup` begins. -Using the :term:`view name` (``buz.txt``) and the type, it asks the -:term:`application registry` this question: +Because it's the "context", the view lookup machinery examines "biz" +to find out what "type" it is. Let's say it finds that the context is +a ``Biz`` type (because "biz" is an instance of the Python class +``Biz``). Using the :term:`view name` (``buz.txt``) and the type, +view lookup asks the :term:`application registry` this question: - Please find me a :term:`view callable` registered with a :term:`view configuration` with the name ``buz.txt`` that can be used for class ``Biz``. -Let's say that question is answered "here you go, here's a bit of code -that is willing to deal with that case", and returns a :term:`view -callable`. The view callable is passed the "biz" object as the -"context" and the current :term:`WebOb` :term:`request` as the -"request". It returns a :term:`response`. - -There are two special cases: - -- During traversal you will often end up with a :term:`view name` that - is the empty string. This indicates that :mod:`repoze.bfg` should - look up the :term:`default view`. The default view is a view that is - registered with no name or a view which is registered with a name - that equals the empty string. - -- If any path segment element begins with the special characters - ``@@`` (think of them as goggles), the value of that segment minus - the goggle characters is considered the :term:`view name` - immediately and traversal stops there. This allows you to address - views that may have the same names as model instance names in the - graph unambiguously. - -.. index:: - pair: debugging; not found errors - -.. index:: - pair: traversal; unicode - -Traversal and Unicode ---------------------- - -The traversal machinery by default attempts to first URL-unquote and -then Unicode-decode each path element in ``PATH_INFO`` from its -natural byte string (``str`` type) representation. URL unquoting is -performed using the Python standard library ``urllib.unquote`` -function. Conversion from a URL-decoded string into Unicode is -attempted using the UTF-8 encoding. If any URL-unquoted path segment -in ``PATH_INFO`` is not decodeable using the UTF-8 decoding, a -TypeError is raised. A segment will be fully URL-unquoted and -UTF8-decoded before it is passed it to the ``__getitem__`` of any -model object during traversal. +Let's say that question is answered by the application registry with +the equivalent of "here you go, here's a bit of code that is willing +to deal with that case"; the application registry returns a +:term:`view callable`. The view callable is then called with the +current :term:`WebOb` :term:`request` as the sole argument: +``request``; it is expected to return a response. + +.. sidebar:: The Example View Callables Accept Only a Request; How Do I Access the Context? + + Most of the examples in this book assume that a view callable is + typically passed only a :term:`request` object. Sometimes your + view callables need access to the :term:`context`, especially when + you use :term:`traversal`. You might use a supported alternate + view callable argument list in your view callables such as the + ``(context, request)`` calling convention described in + :ref:`request_and_context_view_definitions`. But you don't need to + if you don't want to. In view callables that accept only a + request, the :term:`context` found by traversal is available as the + ``context`` attribute of the request object. The :term:`view name` + is available as the ``view_name`` attribute of the request object. + Other :mod:`repoze.bfg` -speficic request attributes are also + available as described in :ref:`special_request_attributes`. References ---------- A tutorial showing how :term:`traversal` can be used to create a :mod:`repoze.bfg` application exists in :ref:`bfg_wiki_tutorial`. + +See the :ref:`views_chapter` chapter for detailed information about +:term:`view lookup`. diff --git a/docs/narr/views.rst b/docs/narr/views.rst index f51e913ba..4a329153c 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -1544,6 +1544,9 @@ to convert non-response return values from a view. The value of the ``attr`` attribute represents the attribute name looked up on the view object to return a response. +.. index:: + pair: debugging; not found errors + .. _debug_notfound_section: :exc:`NotFound` Errors diff --git a/docs/narr/webob.rst b/docs/narr/webob.rst index 906aadb5e..c94063059 100644 --- a/docs/narr/webob.rst +++ b/docs/narr/webob.rst @@ -109,6 +109,8 @@ instance, ``req.if_modified_since`` returns a `datetime .. index:: pair: request attributes; special +.. _special_request_attributes: + Special Attributes Added to the Request by :mod:`repoze.bfg` ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
