diff options
| author | Carlos de la Guardia <cguardia@yahoo.com> | 2011-06-06 12:03:46 -0500 |
|---|---|---|
| committer | Carlos de la Guardia <cguardia@yahoo.com> | 2011-06-06 12:03:46 -0500 |
| commit | a5713863a80a493a1485057609578b907d04c770 (patch) | |
| tree | e2230d20775d9fa80ea8dafa06910b65cb6859d5 /docs | |
| parent | ffca4ef5eba01234433bc4d6029dee719ab261d0 (diff) | |
| parent | 703e3677dc42518cb80626650aaf62e7db17812a (diff) | |
| download | pyramid-a5713863a80a493a1485057609578b907d04c770.tar.gz pyramid-a5713863a80a493a1485057609578b907d04c770.tar.bz2 pyramid-a5713863a80a493a1485057609578b907d04c770.zip | |
Merge branch 'master' of github.com:Pylons/pyramid
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/api/authentication.rst | 2 | ||||
| -rw-r--r-- | docs/designdefense.rst | 61 | ||||
| -rw-r--r-- | docs/glossary.rst | 18 | ||||
| -rw-r--r-- | docs/narr/hybrid.rst | 46 | ||||
| -rw-r--r-- | docs/narr/project.rst | 14 | ||||
| -rw-r--r-- | docs/narr/renderers.rst | 29 | ||||
| -rw-r--r-- | docs/narr/resources.rst | 16 | ||||
| -rw-r--r-- | docs/narr/traversal.rst | 30 | ||||
| -rw-r--r-- | docs/narr/viewconfig.rst | 24 | ||||
| -rw-r--r-- | docs/tutorials/wiki/authorization.rst | 24 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/authorization/tutorial/__init__.py | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/models/tutorial/__init__.py | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/tests/tutorial/tests.py | 10 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/views/tutorial/__init__.py | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki/tests.rst | 2 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/authorization.rst | 81 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/installation.rst | 2 | ||||
| -rw-r--r-- | docs/whatsnew-1.1.rst | 13 |
19 files changed, 246 insertions, 142 deletions
diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst index bf7f8f8d5..5d4dbd9e3 100644 --- a/docs/api/authentication.rst +++ b/docs/api/authentication.rst @@ -14,6 +14,8 @@ Authentication Policies .. autoclass:: RemoteUserAuthenticationPolicy + .. autoclass:: SessionAuthenticationPolicy + Helper Classes ~~~~~~~~~~~~~~ diff --git a/docs/designdefense.rst b/docs/designdefense.rst index 136b9c5de..0321113fa 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -1011,6 +1011,67 @@ which returns Zope3-security-proxy-wrapped objects for each traversed object (including the :term:`context` and the :term:`root`). This would have the effect of creating a more Zope3-like environment without much effort. +.. _simpler_traversal_model: + +Pyramid has Simpler Traversal Machinery than Does Zope +------------------------------------------------------ + +Zope's default traverser: + +- Allows developers to mutate the traversal name stack while traversing (they + can add and remove path elements). + +- Attempts to use an adaptation to obtain the "next" element in the path from + the currently traversed object, falling back to ``__bobo_traverse__``, + ``__getitem__`` and eventually ``__getattr__``. + +Zope's default traverser allows developers to mutate the traversal name stack +during traversal by mutating ``REQUEST['TraversalNameStack']``. Pyramid's +default traverser (``pyramid.traversal.ResourceTreeTraverser``) does not +offer a way to do this; it does not maintain a stack as a request attribute +and, even if it did, it does not pass the request to resource objects while +it's traversing. While it was handy at times, this feature was abused in +frameworks built atop Zope (like CMF and Plone), often making it difficult to +tell exactly what was happening when a traversal didn't match a view. I felt +it was better to make folks that wanted the feature replace the traverser +rather than build that particular honey pot in to the default traverser. + +Zope uses multiple mechanisms to attempt to obtain the next element in the +resource tree based on a name. It first tries an adaptation of the current +resource to ``ITraversable``, and if that fails, it falls back to attempting +number of magic methods on the resource (``__bobo_traverse__``, +``__getitem__``, and ``__getattr__``). My experience while both using Zope +and attempting to reimplement its publisher in ``repoze.zope2`` led me to +believe the following: + +- The *default* traverser should be as simple as possible. Zope's publisher + is somewhat difficult to follow and replicate due to the fallbacks it tried + when one traversal method failed. It is also slow. + +- The *entire traverser* should be replaceable, not just elements of the + traversal machinery. Pyramid has a few "big" components rather than a + plethora of small ones. If the entire traverser is replaceable, it's an + antipattern to make portions of the default traverser replaceable. Doing + so is a "knobs on knobs" pattern, which is unfortunately somewhat endemic + in Zope. In a "knobs on knobs" pattern, a replaceable subcomponent of a + larger component is made configurable using the same configuration + mechanism that can be used to replace the larger component. For example, + in Zope, you can replace the default traverser by registering an adapter. + But you can also (or alternately) control how the default traverser + traverses by registering one or more adapters. As a result of being able + to either replace the larger component entirely or turn knobs on the + default implementation of the larger component, no one understands when (or + whether) they should ever override the larger component entrirely. This + results, over time, in a "rusting together" of the larger "replaceable" + component and the framework itself, because people come to depend on the + availability of the default component in order just to turn its knobs. The + default component effectively becomes part of the framework, which entirely + subverts the goal of making it replaceable. In Pyramid, typically if a + component is replaceable, it will itself have no knobs (it will be "solid + state"). If you want to influence behavior controlled by that component, + you will replace the component instead of turning knobs attached to the + component. + .. _microframeworks_smaller_hello_world: Microframeworks Have Smaller Hello World Programs diff --git a/docs/glossary.rst b/docs/glossary.rst index e1e9e76a9..579d89afd 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -55,7 +55,7 @@ Glossary For example, the asset specification ``my.package:static/baz.css`` identifies the file named ``baz.css`` in the ``static`` subdirectory of the ``my.package`` - Python :term:`package`. See :ref:`asset_specifications` for more + Python :term:`package`. See :ref:`asset_specifications` for more info. package @@ -65,7 +65,7 @@ Glossary module A Python source file; a file on the filesystem that typically ends with - the extension ``.py`` or ``.pyc``. Modules often live in a + the extension ``.py`` or ``.pyc``. Modules often live in a :term:`package`. project @@ -214,9 +214,9 @@ Glossary a :term:`context` resource. A permission is associated with a view name and a resource type by the developer. Resources are decorated with security declarations (e.g. an :term:`ACL`), which reference these - tokens also. Permissions are used by the active to security policy to + tokens also. Permissions are used by the active security policy to match the view permission against the resources's statements about which - permissions are granted to which principal in a context in order to to + permissions are granted to which principal in a context in order to answer the question "is this user allowed to do this". Examples of permissions: ``read``, or ``view_blog_entries``. @@ -356,14 +356,14 @@ Glossary METAL `Macro Expansion for TAL <http://wiki.zope.org/ZPT/METAL>`_, a part of :term:`ZPT` which makes it possible to share common look - and feel between templates. + and feel between templates. Genshi An `XML templating language <http://pypi.python.org/pypi/Genshi/>`_ by Christopher Lenz. Jinja2 - A `text templating language <http://jinja.pocoo.org/2/>`_ by Armin + A `text templating language <http://jinja.pocoo.org/2/>`_ by Armin Ronacher. Routes @@ -401,7 +401,7 @@ Glossary root The object at which :term:`traversal` begins when :app:`Pyramid` searches for a :term:`context` resource (for :term:`URL Dispatch`, the - root is *always* the context resource unless the ``traverse=`` argument + root is *always* the context resource unless the ``traverse=`` argument is used in route configuration). subpath @@ -856,7 +856,7 @@ Glossary ZCML `Zope Configuration Markup Language <http://www.muthukadan.net/docs/zca.html#zcml>`_, an XML dialect - used by Zope and :term:`pyramid_zcml` for configuration tasks. + used by Zope and :term:`pyramid_zcml` for configuration tasks. ZCML directive A ZCML "tag" such as ``<view>`` or ``<route>``. @@ -894,5 +894,5 @@ Glossary http://docs.python.org/distutils/index.html for more information. :term:`setuptools` is actually an *extension* of the Distutils. - + diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index f8ed743fb..97adaeafd 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -175,7 +175,7 @@ match is straightforward. When a route is matched: Root factories related to a route were explained previously within :ref:`route_factories`. Both the global root factory and default root factory were explained previously within - :ref:`the_resource_tree`. + :ref:`the_resource_tree`. .. _using_traverse_in_a_route_pattern: @@ -216,7 +216,7 @@ root factory. Once :term:`traversal` has found a :term:`context` resource, have been invoked in a "pure" traversal-based application. Let's assume there is no *global* :term:`root factory` configured in -this application. The *default* :term:`root factory` cannot be traversed: +this application. The *default* :term:`root factory` cannot be traversed: it has no useful ``__getitem__`` method. So we'll need to associate this route configuration with a custom root factory in order to create a useful hybrid application. To that end, let's imagine that @@ -233,8 +233,8 @@ we've created a root factory that looks like so in a module named def __getitem__(self, name): return self.subobjects[name] - root = Traversable( - {'a':Resource({'b':Resource({'c':Resource({})})})} + root = Resource( + {'a': Resource({'b': Resource({'c': Resource({})})})} ) def root_factory(request): @@ -247,20 +247,20 @@ configuration statement: .. code-block:: python :linenos: - config.add_route('home', '{foo}/{bar}/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') The ``factory`` above points at the function we've defined. It will return -an instance of the ``Traversable`` class as a root object whenever this route -is matched. Instances of the``Resource`` class can be used for tree -traversal because they have a ``__getitem__`` method that does something -nominally useful. Since traversal uses ``__getitem__`` to walk the resources -of a resource tree, using traversal against the root resource implied by our -route statement is a reasonable thing to do. +an instance of the ``Resource`` class as a root object whenever this route is +matched. Instances of the ``Resource`` class can be used for tree traversal +because they have a ``__getitem__`` method that does something nominally +useful. Since traversal uses ``__getitem__`` to walk the resources of a +resource tree, using traversal against the root resource implied by our route +statement is a reasonable thing to do. .. note:: - We could have also used our ``root_factory`` callable as the + We could have also used our ``root_factory`` function as the ``root_factory`` argument of the :class:`~pyramid.config.Configurator` constructor, instead of associating it with a particular route inside the route's @@ -279,12 +279,12 @@ instance named ``root`` in ``routes.py``. If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse``, is ``http://example.com/one/two/a/b/c``, the traversal path used against the root object will be ``a/b/c``. As a result, -:app:`Pyramid` will attempt to traverse through the edges ``a``, -``b``, and ``c``, beginning at the root object. +:app:`Pyramid` will attempt to traverse through the edges ``'a'``, +``'b'``, and ``'c'``, beginning at the root object. In our above example, this particular set of traversal steps will mean that -the :term:`context` resource of the view would be the ``Traversable`` object -we've named ``c`` in our bogus resource tree and the :term:`view name` +the :term:`context` resource of the view would be the ``Resource`` object +we've named ``'c'`` in our bogus resource tree and the :term:`view name` resulting from traversal will be the empty string; if you need a refresher about why this outcome is presumed, see :ref:`traversal_algorithm`. @@ -297,7 +297,7 @@ invoked after a route matches: .. code-block:: python :linenos: - config.add_route('home', '{foo}/{bar}/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') config.add_view('mypackage.views.myview', route_name='home') @@ -327,11 +327,11 @@ when a hybrid route is matched: .. code-block:: python :linenos: - config.add_route('home', '{foo}/{bar}/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') - config.add_view('mypackage.views.myview', name='home') - config.add_view('mypackage.views.another_view', name='another', - route_name='home') + config.add_view('mypackage.views.myview', route_name='home') + config.add_view('mypackage.views.another_view', route_name='home', + name='another') The ``add_view`` call for ``mypackage.views.another_view`` above names a different view and, more importantly, a different :term:`view name`. The @@ -373,12 +373,12 @@ Here's a use of the ``traverse`` pattern in a call to :linenos: config.add_route('abc', '/articles/{article}/edit', - traverse='/articles/{article}') + traverse='/{article}') The syntax of the ``traverse`` argument is the same as it is for ``pattern``. -If, as above, the ``pattern`` provided is ``articles/{article}/edit``, +If, as above, the ``pattern`` provided is ``/articles/{article}/edit``, and the ``traverse`` argument provided is ``/{article}``, when a request comes in that causes the route to match in such a way that the ``article`` match value is ``1`` (when the request URI is diff --git a/docs/narr/project.rst b/docs/narr/project.rst index c1558266a..631412f42 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -23,7 +23,7 @@ what type of application you're trying to construct. These scaffolds are rendered using the :term:`PasteDeploy` ``paster`` script. .. index:: - single: scaffolds + single: scaffolds single: pyramid_starter scaffold single: pyramid_zodb scaffold single: pyramid_alchemy scaffold @@ -55,7 +55,7 @@ The included scaffolds are these: ``pyramid_zodb`` URL mapping via :term:`traversal` and persistence via :term:`ZODB`. -``pyramid_routesalchemy`` +``pyramid_routesalchemy`` URL mapping via :term:`URL dispatch` and persistence via :term:`SQLAlchemy` @@ -281,7 +281,7 @@ name ``MyProject`` as a section name: .. code-block:: text [chrism@vitaminf shellenv]$ ../bin/paster pshell development.ini MyProject - Python 2.4.5 (#1, Aug 29 2008, 12:27:37) + Python 2.4.5 (#1, Aug 29 2008, 12:27:37) [GCC 4.0.1 (Apple Inc. build 5465)] on darwin Type "help" for more information. "root" is the Pyramid app root object, "registry" is the Pyramid registry object. @@ -335,7 +335,7 @@ example, if you have the following ``.ini`` file content: default_locale_name = en [pipeline:main] - pipeline = + pipeline = egg:WebError#evalerror MyProject @@ -606,7 +606,7 @@ for each request. .. note:: - In general, :app:`Pyramid` applications generated from scaffolds + In general, :app:`Pyramid` applications generated from scaffolds should be threading-aware. It is not required that a :app:`Pyramid` application be nonblocking as all application code will run in its own thread, provided by the server you're using. @@ -622,13 +622,13 @@ implementations. Such a section should consists of global parameters that are shared by all the applications, servers and :term:`middleware` defined within the configuration file. The values in a ``[DEFAULT]`` section will be passed - to your application's ``main`` function as ``global_values`` (see + to your application's ``main`` function as ``global_config`` (see the reference to the ``main`` function in :ref:`init_py`). ``production.ini`` ~~~~~~~~~~~~~~~~~~~ -The ``development.ini`` file is a :term:`PasteDeploy` configuration file with +The ``production.ini`` file is a :term:`PasteDeploy` configuration file with a purpose much like that of ``development.ini``. However, it disables the WebError interactive debugger, replacing it with a logger which outputs exception messages to ``stderr`` by default. It also turns off template diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst index c3533648b..b284fe73f 100644 --- a/docs/narr/renderers.rst +++ b/docs/narr/renderers.rst @@ -11,7 +11,6 @@ Response interface, :app:`Pyramid` will attempt to use a .. code-block:: python :linenos: - from pyramid.response import Response from pyramid.view import view_config @view_config(renderer='json') @@ -193,11 +192,11 @@ You can configure a view to use the JSON renderer by naming ``json`` as the .. code-block:: python :linenos: - config.add_view('myproject.views.hello_world', + config.add_view('myproject.views.hello_world', name='hello', context='myproject.resources.Hello', renderer='json') - + Views which use the JSON renderer can vary non-body response attributes by using the api of the ``request.response`` attribute. See @@ -221,7 +220,7 @@ See :ref:`chameleon_zpt_templates` for more information about ZPT templates. If the ``renderer`` attribute of a view configuration is an absolute path or a :term:`asset specification` which has a final path element with a filename extension of ``.txt``, the :term:`Chameleon` text renderer is used. See -:ref:`chameleon_zpt_templates` for more information about Chameleon text +:ref:`chameleon_text_templates` for more information about Chameleon text templates. The behavior of these renderers is the same, except for the engine @@ -239,7 +238,7 @@ dictionary, an error will be raised. Before passing keywords to the template, the keyword arguments derived from the dictionary returned by the view are augmented. The callable object -- -whatever object was used to define the ``view`` -- will be automatically +whatever object was used to define the view -- will be automatically inserted into the set of keyword arguments passed to the template as the ``view`` keyword. If the view callable was a class, the ``view`` keyword will be an instance of that class. Also inserted into the keywords passed to @@ -286,7 +285,7 @@ the API of the ``request.response`` attribute. See The ``Mako`` template renderer renders views using a Mako template. When used, the view must return a Response object or a Python *dictionary*. The dictionary items will then be used in the global template space. If the view -callable returns anything but a Response object, or a dictionary, an error +callable returns anything but a Response object or a dictionary, an error will be raised. When using a ``renderer`` argument to a :term:`view configuration` to specify @@ -448,15 +447,15 @@ following interface: class RendererFactory: def __init__(self, info): - """ Constructor: info will be an object having the the + """ Constructor: info will be an object having the following attributes: name (the renderer name), package (the package that was 'current' at the time the renderer was registered), type (the renderer type name), registry (the current application registry) and - settings (the deployment settings dictionary). """ + settings (the deployment settings dictionary). """ def __call__(self, value, system): - """ Call a the renderer implementation with the value + """ Call the renderer implementation with the value and the system value passed in as arguments and return the result (a string or unicode object). The value is the return value of a view. The system value is a @@ -468,7 +467,7 @@ factory constructor is available as :class:`pyramid.interfaces.IRendererInfo`. There are essentially two different kinds of renderer factories: -- A renderer factory which expects to accept a :term:`asset +- A renderer factory which expects to accept an :term:`asset specification`, or an absolute path, as the ``name`` attribute of the ``info`` object fed to its constructor. These renderer factories are registered with a ``name`` value that begins with a dot (``.``). These @@ -529,7 +528,7 @@ factory, which expects to be passed a filesystem path: .. code-block:: python :linenos: - config.add_renderer(name='.jinja2', + config.add_renderer(name='.jinja2', factory='my.package.MyJinja2Renderer') Adding the above code to your application startup will allow you to use the @@ -559,7 +558,7 @@ to the ``MyJinja2Renderer`` constructor will be the full value that was set as ``renderer=`` in the view configuration. Changing an Existing Renderer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can associate more than one filename extension with the same existing renderer implementation as necessary if you need to use a different file @@ -626,9 +625,9 @@ sets an ``override_renderer`` attribute on the request itself, which is the def set_xmlrpc_params(event): request = event.request if (request.content_type == 'text/xml' - and request.method == 'POST' - and not 'soapaction' in request.headers - and not 'x-pyramid-avoid-xmlrpc' in request.headers): + and request.method == 'POST' + and not 'soapaction' in request.headers + and not 'x-pyramid-avoid-xmlrpc' in request.headers): params, method = parse_xmlrpc_request(request) request.xmlrpc_params, request.xmlrpc_method = params, method request.is_xmlrpc = True diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst index a11466a87..fa8ccc549 100644 --- a/docs/narr/resources.rst +++ b/docs/narr/resources.rst @@ -91,9 +91,9 @@ Here's a sample resource tree, represented by a variable named ``root``: root = Resource({'a':Resource({'b':Resource({'c':Resource()})})}) The resource tree we've created above is represented by a dictionary-like -root object which has a single child named ``a``. ``a`` has a single child -named ``b``, and ``b`` has a single child named ``c``, which has no children. -It is therefore possible to access ``c`` like so: +root object which has a single child named ``'a'``. ``'a'`` has a single child +named ``'b'``, and ``'b'`` has a single child named ``'c'``, which has no +children. It is therefore possible to access the ``'c'`` leaf resource like so: .. code-block:: python :linenos: @@ -101,7 +101,7 @@ It is therefore possible to access ``c`` like so: root['a']['b']['c'] If you returned the above ``root`` object from a :term:`root factory`, the -path ``/a/b/c`` would find the ``c`` object in the resource tree as the +path ``/a/b/c`` would find the ``'c'`` object in the resource tree as the result of :term:`traversal`. In this example, each of the resources in the tree is of the same class. @@ -428,7 +428,7 @@ list, we will get: .. code-block:: python :linenos: - + list(lineage(thing2)) [ <Thing object at thing2>, <Thing object at thing1> ] @@ -437,8 +437,8 @@ resource it was passed unconditionally. Then, if the resource supplied a ``__parent__`` attribute, it returns the resource represented by ``resource.__parent__``. If *that* resource has a ``__parent__`` attribute, return that resource's parent, and so on, until the resource being inspected -either has no ``__parent__`` attribute or which has a ``__parent__`` -attribute of ``None``. +either has no ``__parent__`` attribute or has a ``__parent__`` attribute of +``None``. See the documentation for :func:`pyramid.location.lineage` for more information. @@ -563,6 +563,7 @@ To do so, use the :func:`zope.interface.directlyProvides` function: .. code-block:: python :linenos: + import datetime from zope.interface import directlyProvides from zope.interface import Interface @@ -587,6 +588,7 @@ the :func:`zope.interface.alsoProvides` function: .. code-block:: python :linenos: + import datetime from zope.interface import alsoProvides from zope.interface import directlyProvides from zope.interface import Interface diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index b3747be61..e1715dc25 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -12,7 +12,7 @@ file system. Traversal walks down the path until it finds a published resource, analogous to a file system "directory" or "file". The resource found as the result of a traversal becomes the :term:`context` of the :term:`request`. Then, the :term:`view lookup` -subsystem is used to find some view code willing "publish" this +subsystem is used to find some view code willing to "publish" this resource by generating a :term:`response`. Using :term:`Traversal` to map a URL to code is optional. It is often @@ -49,17 +49,17 @@ For example, if the path info sequence is ``['a', 'b', 'c']``: can be configured to return whatever object is appropriate as the traversal root of your application. -- Next, the first element (``a``) is popped from the path segment +- Next, the first element (``'a'``) is popped from the path segment sequence and is used as a key to lookup the corresponding resource in the root. This invokes the root resource's ``__getitem__`` method - using that value (``a``) as an argument. + using that value (``'a'``) as an argument. -- If the root resource "contains" a resource with key ``a``, its +- If the root resource "contains" a resource with key ``'a'``, its ``__getitem__`` method will return it. The :term:`context` temporarily becomes the "A" resource. -- The next segment (``b``) is popped from the path sequence, and the "A" - resource's ``__getitem__`` is called with that value (``b``) as an +- The next segment (``'b'``) is popped from the path sequence, and the "A" + resource's ``__getitem__`` is called with that value (``'b'``) as an argument; we'll presume it succeeds. - The "A" resource's ``__getitem__`` returns another resource, which @@ -78,7 +78,7 @@ The results of a :term:`traversal` also include a :term:`view name`. If traversal ends before the path segment sequence is exhausted, the :term:`view name` is the *next* remaining path segment element. If the :term:`traversal` expends all of the path segments, then the :term:`view -name` is the empty string (`''`). +name` is the empty string (``''``). The combination of the context resource and the :term:`view name` found via traversal is used later in the same request by the :term:`view @@ -263,26 +263,26 @@ system uses this algorithm to find a :term:`context` resource and a 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 resource during traversal. + in to the ``__getitem__`` of any resource during traversal. Thus, a request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the traversal sequence ``[u'a', u'b', u'c']``. #. :term:`Traversal` begins at the root resource returned by the root factory. For the traversal sequence ``[u'a', u'b', u'c']``, the root - resource's ``__getitem__`` is called with the name ``a``. Traversal + resource's ``__getitem__`` is called with the name ``'a'``. Traversal continues through the sequence. In our example, if the root resource's ``__getitem__`` called with the name ``a`` returns a resource (aka - "resource ``a``"), that resource's ``__getitem__`` is called with the - name ``b``. If resource A returns a resource when asked for ``b``, - "resource ``b``"'s ``__getitem__`` is then asked for the name ``c``, and - may return "resource ``c``". + resource "A"), that resource's ``__getitem__`` is called with the name + ``'b'``. If resource "A" returns a resource "B" when asked for ``'b'``, + resource B's ``__getitem__`` is then asked for the name ``'c'``, and may + return resource "C". #. Traversal ends when a) the entire path is exhausted or b) when any resouce raises a :exc:`KeyError` from its ``__getitem__`` or c) when any non-final path element traversal does not have a ``__getitem__`` method - (resulting in a :exc:`NameError`) or d) when any path element is prefixed - with the set of characters ``@@`` (indicating that the characters + (resulting in a :exc:`AttributeError`) or d) when any path element is + prefixed with the set of characters ``@@`` (indicating that the characters following the ``@@`` token should be treated as a :term:`view name`). #. When traversal ends for any of the reasons in the previous step, the last diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index d99e5bed5..5640800a2 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -77,7 +77,7 @@ All forms of view configuration accept the same general types of arguments. Many arguments supplied during view configuration are :term:`view predicate` arguments. View predicate arguments used during view configuration are used to narrow the set of circumstances in which :term:`view lookup` will find a -particular view callable. +particular view callable. In general, the fewer number of predicates which are supplied to a particular view configuration, the more likely it is that the associated @@ -112,7 +112,7 @@ Non-Predicate Arguments The name of a :term:`permission` that the user must possess in order to invoke the :term:`view callable`. See :ref:`view_security_section` for more information about view security and permissions. - + If ``permission`` is not supplied, no permission is registered for this view (it's accessible by any caller). @@ -183,7 +183,7 @@ Non-Predicate Arguments argument. The view callable it is passed will accept ``(context, request)``. The decorator must return a replacement view callable which also accepts ``(context, request)``. - + ``mapper`` A Python object or :term:`dotted Python name` which refers to a :term:`view mapper`, or ``None``. By default it is ``None``, which indicates that the @@ -228,7 +228,7 @@ configured view. ``pattern``, representing a part of the path that will be used by :term:`traversal` against the result of the route's :term:`root factory`. - If ``route_name`` is not supplied, the view callable will be have a chance + If ``route_name`` is not supplied, the view callable will only have a chance of being invoked if no other route was matched. This is when the request/context pair found via :term:`resource location` does not indicate it matched any configured route. @@ -400,7 +400,7 @@ configuration stanza: .. code-block:: python :linenos: - config.add_view('mypackage.views.my_view', name='my_view', request_method='POST', + config.add_view('mypackage.views.my_view', name='my_view', request_method='POST', context=MyResource, permission='read') All arguments to ``view_config`` may be omitted. For example: @@ -517,7 +517,7 @@ registration. For example: This registers the same view under two different names. -The decorator can also be used against class methods: +The decorator can also be used against a method of a class: .. code-block:: python :linenos: @@ -533,9 +533,9 @@ The decorator can also be used against class methods: def amethod(self): return Response('hello') -When the decorator is used against a class method, a view is registered for -the *class*, so the class constructor must accept an argument list in one of -two forms: either it must accept a single argument ``request`` or it must +When the decorator is used against a method of a class, a view is registered +for the *class*, so the class constructor must accept an argument list in one +of two forms: either it must accept a single argument ``request`` or it must accept two arguments, ``context, request``. The method which is decorated must return a :term:`response`. @@ -760,7 +760,7 @@ Here is an example for a simple view configuration using :term:`traversal`: URL = /FrontPage context: <tutorial.models.Page object at 0xa12536c> - view name: + view name: View: ----- @@ -791,7 +791,7 @@ A more complex configuration might generate something like this: route name: about route pattern: /about route path: /about - subpath: + subpath: route predicates (request method = GET) View: @@ -805,7 +805,7 @@ A more complex configuration might generate something like this: route name: about_post route pattern: /about route path: /about - subpath: + subpath: route predicates (request method = POST) View: diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index e4480d6d9..8781325d2 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -32,10 +32,17 @@ Adding Authentication and Authorization Policies We'll change our package's ``__init__.py`` file to enable an ``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable -declarative security checking. When you're done, your ``__init__.py`` will -look like so: +declarative security checking. We need to import the new policies: + +.. literalinclude:: src/authorization/tutorial/__init__.py + :lines: 4-5,8 + :linenos: + :language: python + +Then, we'll add those policies to the configuration: .. literalinclude:: src/authorization/tutorial/__init__.py + :lines: 16-18,26-28 :linenos: :language: python @@ -46,6 +53,13 @@ by this policy: it is required. The ``callback`` is a reference to a ``groupfinder`` function in the ``tutorial`` package's ``security.py`` file. We haven't added that module yet, but we're about to. +When you're done, your ``__init__.py`` will +look like so: + +.. literalinclude:: src/authorization/tutorial/__init__.py + :linenos: + :language: python + Adding ``security.py`` ~~~~~~~~~~~~~~~~~~~~~~ @@ -57,12 +71,12 @@ content: :linenos: :language: python -The ``groupfinder`` function defined here is an authorization policy +The ``groupfinder`` function defined here is an :term:`authentication policy` "callback"; it is a callable that accepts a userid and a request. If the -userid exists in the set of users known by the system, the callback will +userid exists in the system, the callback will return a sequence of group identifiers (or an empty sequence if the user isn't a member of any groups). If the userid *does not* exist in the system, -the callback will return ``None``. In a production system this data will +the callback will return ``None``. In a production system, user and group data will most often come from a database, but here we use "dummy" data to represent user and groups sources. Note that the ``editor`` user is a member of the ``group:editors`` group in our dummy group data (the ``GROUPS`` data diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py index 3e9266754..f7dab5f47 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py @@ -16,8 +16,8 @@ def main(global_config, **settings): authn_policy = AuthTktAuthenticationPolicy(secret='sosecret', callback=groupfinder) authz_policy = ACLAuthorizationPolicy() - zodb_uri = settings.get('zodb_uri') - if zodb_uri is None: + zodb_uri = settings.get('zodb_uri', False) + if zodb_uri is False: raise ValueError("No 'zodb_uri' in application configuration.") finder = PersistentApplicationFinder(zodb_uri, appmaker) diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py index a9f776980..6a4093a3b 100644 --- a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py @@ -5,8 +5,8 @@ from tutorial.models import appmaker def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - zodb_uri = settings.get('zodb_uri') - if zodb_uri is None: + zodb_uri = settings.get('zodb_uri', False) + if zodb_uri is False: raise ValueError("No 'zodb_uri' in application configuration.") finder = PersistentApplicationFinder(zodb_uri, appmaker) diff --git a/docs/tutorials/wiki/src/models/tutorial/__init__.py b/docs/tutorials/wiki/src/models/tutorial/__init__.py index bf0f683bf..73fc81d23 100644 --- a/docs/tutorials/wiki/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/models/tutorial/__init__.py @@ -5,8 +5,8 @@ from tutorial.models import appmaker def main(global_config, **settings): """ This function returns a WSGI application. """ - zodb_uri = settings.get('zodb_uri') - if zodb_uri is None: + zodb_uri = settings.get('zodb_uri', False) + if zodb_uri is False: raise ValueError("No 'zodb_uri' in application configuration.") finder = PersistentApplicationFinder(zodb_uri, appmaker) diff --git a/docs/tutorials/wiki/src/tests/tutorial/tests.py b/docs/tutorials/wiki/src/tests/tutorial/tests.py index d9ff866f1..0ce5ea718 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/tests.py +++ b/docs/tutorials/wiki/src/tests/tutorial/tests.py @@ -139,16 +139,20 @@ class FunctionalTests(unittest.TestCase): self.tmpdir = tempfile.mkdtemp() dbpath = os.path.join( self.tmpdir, 'test.db') - settings = { 'zodb_uri' : 'file://' + dbpath } + from repoze.zodbconn.uri import db_from_uri + db = db_from_uri('file://' + dbpath) + settings = { 'zodb_uri' : None } app = main({}, **settings) - from repoze.zodbconn.middleware import EnvironmentDeleterMiddleware - app = EnvironmentDeleterMiddleware(app) + from repoze.zodbconn.connector import Connector + app = Connector(app, db) + self.db = db from webtest import TestApp self.testapp = TestApp(app) def tearDown(self): import shutil + self.db.close() shutil.rmtree( self.tmpdir ) def test_root(self): diff --git a/docs/tutorials/wiki/src/views/tutorial/__init__.py b/docs/tutorials/wiki/src/views/tutorial/__init__.py index 91f7c2624..04a01fead 100644 --- a/docs/tutorials/wiki/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/views/tutorial/__init__.py @@ -5,8 +5,8 @@ from tutorial.models import appmaker def main(global_config, **settings): """ This function returns a WSGI application. """ - zodb_uri = settings.get('zodb_uri') - if zodb_uri is None: + zodb_uri = settings.get('zodb_uri', False) + if zodb_uri is False: raise ValueError("No 'zodb_uri' in application configuration.") finder = PersistentApplicationFinder(zodb_uri, appmaker) diff --git a/docs/tutorials/wiki/tests.rst b/docs/tutorials/wiki/tests.rst index f3151dbcc..c843a0129 100644 --- a/docs/tutorials/wiki/tests.rst +++ b/docs/tutorials/wiki/tests.rst @@ -73,6 +73,6 @@ The expected result looks something like: ......... ---------------------------------------------------------------------- - Ran 9 tests in 0.203s + Ran 23 tests in 1.653s OK diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index b1d3b0001..64c587f07 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -9,10 +9,23 @@ view, edit, and add pages to our wiki. For purposes of demonstration we'll change our application to allow only people whom possess a specific username (`editor`) to add and edit wiki pages but we'll continue allowing anyone with access to the server to view pages. -:app:`Pyramid` provides facilities for *authorization* and -*authentication*. We'll make use of both features to provide security +:app:`Pyramid` provides facilities for :term:`authorization` and +:term:`authentication`. We'll make use of both features to provide security to our application. +We will add an :term:`authentication policy` and an +:term:`authorization policy` to our :term:`application +registry`, add a ``security.py`` module, create a :term:`root factory` +with an :term:`ACL`, and add :term:`permission` declarations to +the ``edit_page`` and ``add_page`` views. + +Then we will add ``login`` and ``logout`` views, and modify the +existing views to make them return a ``logged_in`` flag to the +renderer. + +Finally, we will add a ``login.pt`` template and change the existing +``view.pt`` and ``edit.pt`` to show a "Logout" link when not logged in. + The source code for this tutorial stage can be browsed at `http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/authorization/ <http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/authorization/>`_. @@ -147,7 +160,7 @@ and adding views, your application's ``__init__.py`` will look like this: :language: python Adding ``security.py`` -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- Add a ``security.py`` module within your package (in the same directory as :file:`__init__.py`, :file:`views.py`, etc) with the following content: @@ -156,7 +169,7 @@ Add a ``security.py`` module within your package (in the same directory as :linenos: :language: python -The groupfinder defined here is an :term:`authentication policy` +The ``groupfinder`` function defined here is an :term:`authentication policy` "callback"; it is a callable that accepts a userid and a request. If the userid exists in the system, the callback will return a sequence of group identifiers (or an empty sequence if the user isn't a member @@ -176,7 +189,7 @@ and the permission associated with the ``add_page`` and ``edit_page`` views, the ``editor`` user should be able to add and edit pages. Adding Login and Logout Views -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------- We'll add a ``login`` view callable which renders a login form and processes the post from the login form, checking credentials. @@ -195,7 +208,7 @@ content: :language: python Changing Existing Views -~~~~~~~~~~~~~~~~~~~~~~~ +----------------------- Then we need to change each of our ``view_page``, ``edit_page`` and ``add_page`` views in ``views.py`` to pass a "logged in" parameter to its @@ -221,7 +234,7 @@ We'll then change the return value of these views to pass the `resulting edit_url = edit_url) Adding the ``login.pt`` Template -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------- Add a ``login.pt`` template to your templates directory. It's referred to within the login view we just added to ``login.py``. @@ -230,7 +243,7 @@ referred to within the login view we just added to ``login.py``. :language: xml Change ``view.pt`` and ``edit.pt`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------------- We'll also need to change our ``edit.pt`` and ``view.pt`` templates to display a "Logout" link if someone is logged in. This link will @@ -245,6 +258,25 @@ class="app-welcome align-right">`` div: <a href="${request.application_url}/logout">Logout</a> </span> +Seeing Our Changes To ``views.py`` and our Templates +---------------------------------------------------- + +Our ``views.py`` module will look something like this when we're done: + +.. literalinclude:: src/authorization/tutorial/views.py + :linenos: + :language: python + +Our ``edit.pt`` template will look something like this when we're done: + +.. literalinclude:: src/authorization/tutorial/templates/edit.pt + :language: xml + +Our ``view.pt`` template will look something like this when we're done: + +.. literalinclude:: src/authorization/tutorial/templates/view.pt + :language: xml + Viewing the Application in a Browser ------------------------------------ @@ -272,31 +304,8 @@ try are as follows: credentials with the username ``editor``, password ``editor`` will display the edit page form. -Seeing Our Changes To ``views.py`` and our Templates ----------------------------------------------------- - -Our ``views.py`` module will look something like this when we're done: - -.. literalinclude:: src/authorization/tutorial/views.py - :linenos: - :language: python - -Our ``edit.pt`` template will look something like this when we're done: - -.. literalinclude:: src/authorization/tutorial/templates/edit.pt - :language: xml - -Our ``view.pt`` template will look something like this when we're done: - -.. literalinclude:: src/authorization/tutorial/templates/view.pt - :language: xml - -Revisiting the Application ---------------------------- - -When we revisit the application in a browser, and log in (as a result -of hitting an edit or add page and submitting the login form with the -``editor`` credentials), we'll see a Logout link in the upper right -hand corner. When we click it, we're logged out, and redirected back -to the front page. - +- After logging in (as a result of hitting an edit or add page + and submitting the login form with the ``editor`` + credentials), we'll see a Logout link in the upper right hand + corner. When we click it, we're logged out, and redirected + back to the front page. diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index b5c73e9c5..5f5b0c216 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -73,7 +73,7 @@ Preparation, Windows .. code-block:: text - c:\pyramidtut> Scripts\easy_install -i docutils \ + c:\pyramidtut> Scripts\easy_install docutils \ nose coverage zope.sqlalchemy SQLAlchemy repoze.tm2 diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst index 992e0b637..ce2f7210a 100644 --- a/docs/whatsnew-1.1.rst +++ b/docs/whatsnew-1.1.rst @@ -53,6 +53,10 @@ Static Routes Minor Feature Additions ----------------------- +- New authentication policy: + :class:`pyramid.authentication.SessionAuthenticationPolicy`, which uses a + session to store credentials. + - Integers and longs passed as ``elements`` to :func:`pyramid.url.resource_url` or :meth:`pyramid.request.Request.resource_url` e.g. ``resource_url(context, @@ -90,6 +94,15 @@ Minor Feature Additions Deprecations and Behavior Differences ------------------------------------- +- The default Mako renderer is now configured to escape all HTML in + expression tags. This is intended to help prevent XSS attacks caused by + rendering unsanitized input from users. To revert this behavior in user's + templates, they need to filter the expression through the 'n' filter:: + + ${ myhtml | n }. + + See https://github.com/Pylons/pyramid/issues/193. + - Deprecated all assignments to ``request.response_*`` attributes (for example ``request.response_content_type = 'foo'`` is now deprecated). Assignments and mutations of assignable request attributes that were |
