summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/api/config.rst5
-rw-r--r--docs/api/request.rst1
-rw-r--r--docs/api/static.rst6
-rw-r--r--docs/narr/assets.rst15
-rw-r--r--docs/narr/extconfig.rst104
-rw-r--r--docs/narr/security.rst60
-rw-r--r--docs/narr/urldispatch.rst58
7 files changed, 227 insertions, 22 deletions
diff --git a/docs/api/config.rst b/docs/api/config.rst
index 48dd2f0b9..ae913d32c 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -132,3 +132,8 @@
are being used.
.. autoclass:: not_
+
+.. attribute:: PHASE0_CONFIG
+.. attribute:: PHASE1_CONFIG
+.. attribute:: PHASE2_CONFIG
+.. attribute:: PHASE3_CONFIG
diff --git a/docs/api/request.rst b/docs/api/request.rst
index dd68fa09c..b325ad076 100644
--- a/docs/api/request.rst
+++ b/docs/api/request.rst
@@ -369,3 +369,4 @@
that used as ``request.GET``, ``request.POST``, and ``request.params``),
see :class:`pyramid.interfaces.IMultiDict`.
+.. autofunction:: apply_request_extensions(request)
diff --git a/docs/api/static.rst b/docs/api/static.rst
index 543e526ad..b6b279139 100644
--- a/docs/api/static.rst
+++ b/docs/api/static.rst
@@ -9,6 +9,12 @@
:members:
:inherited-members:
+ .. autoclass:: PathSegmentCacheBuster
+ :members:
+
+ .. autoclass:: QueryStringCacheBuster
+ :members:
+
.. autoclass:: PathSegmentMd5CacheBuster
:members:
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index fc908c2b4..d6bc8cbb8 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -446,19 +446,20 @@ In order to implement your own cache buster, you can write your own class from
scratch which implements the :class:`~pyramid.interfaces.ICacheBuster`
interface. Alternatively you may choose to subclass one of the existing
implementations. One of the most likely scenarios is you'd want to change the
-way the asset token is generated. To do this just subclass an existing
-implementation and replace the :meth:`~pyramid.interfaces.ICacheBuster.token`
-method. Here is an example which just uses Git to get the hash of the
-currently checked out code:
+way the asset token is generated. To do this just subclass either
+:class:`~pyramid.static.PathSegmentCacheBuster` or
+:class:`~pyramid.static.QueryStringCacheBuster` and define a
+``tokenize(pathspec)`` method. Here is an example which just uses Git to get
+the hash of the currently checked out code:
.. code-block:: python
:linenos:
import os
import subprocess
- from pyramid.static import PathSegmentMd5CacheBuster
+ from pyramid.static import PathSegmentCacheBuster
- class GitCacheBuster(PathSegmentMd5CacheBuster):
+ class GitCacheBuster(PathSegmentCacheBuster):
"""
Assuming your code is installed as a Git checkout, as opposed to as an
egg from an egg repository like PYPI, you can use this cachebuster to
@@ -470,7 +471,7 @@ currently checked out code:
['git', 'rev-parse', 'HEAD'],
cwd=here).strip()
- def token(self, pathspec):
+ def tokenize(self, pathspec):
return self.sha1
Choosing a Cache Buster
diff --git a/docs/narr/extconfig.rst b/docs/narr/extconfig.rst
index 6587aef92..a61eca7b7 100644
--- a/docs/narr/extconfig.rst
+++ b/docs/narr/extconfig.rst
@@ -215,13 +215,115 @@ registers an action with a higher order than the
passed to it, that a route by this name was already registered by
``add_route``, and if such a route has not already been registered, it's a
configuration error (a view that names a nonexistent route via its
-``route_name`` parameter will never be called).
+``route_name`` parameter will never be called). As of Pyramid 1.6 it is
+possible for one action to invoke another. See :ref:`ordering_actions` for
+more information.
``introspectables`` is a sequence of :term:`introspectable` objects. You can
pass a sequence of introspectables to the
:meth:`~pyramid.config.Configurator.action` method, which allows you to
augment Pyramid's configuration introspection system.
+.. _ordering_actions:
+
+Ordering Actions
+----------------
+
+In Pyramid every :term:`action` has an inherent ordering relative to other
+actions. The logic within actions is deferred until a call to
+:meth:`pyramid.config.Configurator.commit` (which is automatically invoked by
+:meth:`pyramid.config.Configurator.make_wsgi_app`). This means you may call
+``config.add_view(route_name='foo')`` **before**
+``config.add_route('foo', '/foo')`` because nothing actually happens until
+commit-time. During a commit cycle conflicts are resolved, actions are ordered
+and executed.
+
+By default, almost every action in Pyramid has an ``order`` of
+:const:`pyramid.config.PHASE3_CONFIG`. Every action within the same order-level
+will be executed in the order it was called.
+This means that if an action must be reliably executed before or after another
+action, the ``order`` must be defined explicitly to make this work. For
+example, views are dependent on routes being defined. Thus the action created
+by :meth:`pyramid.config.Configurator.add_route` has an ``order`` of
+:const:`pyramid.config.PHASE2_CONFIG`.
+
+Pre-defined Phases
+~~~~~~~~~~~~~~~~~~
+
+:const:`pyramid.config.PHASE0_CONFIG`
+
+- This phase is reserved for developers who want to execute actions prior
+ to Pyramid's core directives.
+
+:const:`pyramid.config.PHASE1_CONFIG`
+
+- :meth:`pyramid.config.Configurator.add_renderer`
+- :meth:`pyramid.config.Configurator.add_route_predicate`
+- :meth:`pyramid.config.Configurator.add_subscriber_predicate`
+- :meth:`pyramid.config.Configurator.add_view_predicate`
+- :meth:`pyramid.config.Configurator.set_authorization_policy`
+- :meth:`pyramid.config.Configurator.set_default_permission`
+- :meth:`pyramid.config.Configurator.set_view_mapper`
+
+:const:`pyramid.config.PHASE2_CONFIG`
+
+- :meth:`pyramid.config.Configurator.add_route`
+- :meth:`pyramid.config.Configurator.set_authentication_policy`
+
+:const:`pyramid.config.PHASE3_CONFIG`
+
+- The default for all builtin or custom directives unless otherwise specified.
+
+Calling Actions From Actions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.6
+
+Pyramid's configurator allows actions to be added during a commit-cycle as
+long as they are added to the current or a later ``order`` phase. This means
+that your custom action can defer decisions until commit-time and then do
+things like invoke :meth:`pyramid.config.Configurator.add_route`. It can also
+provide better conflict detection if your addon needs to call more than one
+other action.
+
+For example, let's make an addon that invokes ``add_route`` and ``add_view``,
+but we want it to conflict with any other call to our addon:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.config import PHASE0_CONFIG
+
+ def includeme(config):
+ config.add_directive('add_auto_route', add_auto_route)
+
+ def add_auto_route(config, name, view):
+ def register():
+ config.add_view(route_name=name, view=view)
+ config.add_route(name, '/' + name)
+ config.action(('auto route', name), register, order=PHASE0_CONFIG)
+
+Now someone else can use your addon and be informed if there is a conflict
+between this route and another, or two calls to ``add_auto_route``.
+Notice how we had to invoke our action **before** ``add_view`` or
+``add_route``. If we tried to invoke this afterward, the subsequent calls to
+``add_view`` and ``add_route`` would cause conflicts because that phase had
+already been executed, and the configurator cannot go back in time to add more
+views during that commit-cycle.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.config import Configurator
+
+ def main(global_config, **settings):
+ config = Configurator()
+ config.include('auto_route_addon')
+ config.add_auto_route('foo', my_view)
+
+ def my_view(request):
+ return request.response
+
.. _introspection:
Adding Configuration Introspection
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 2dc0c76af..75f4dc7c5 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -341,9 +341,7 @@ third argument is a permission or sequence of permission names.
A principal is usually a user id, however it also may be a group id if your
authentication system provides group information and the effective
:term:`authentication policy` policy is written to respect group information.
-For example, the
-:class:`pyramid.authentication.RepozeWho1AuthenticationPolicy` respects group
-information if you configure it with a ``callback``.
+See :ref:`extending_default_authentication_policies`.
Each ACE in an ACL is processed by an authorization policy *in the
order dictated by the ACL*. So if you have an ACL like this:
@@ -583,6 +581,60 @@ via print statements when a call to
:meth:`~pyramid.request.Request.has_permission` fails is often useful.
.. index::
+ single: authentication policy (extending)
+
+.. _extending_default_authentication_policies:
+
+Extending Default Authentication Policies
+-----------------------------------------
+
+Pyramid ships with some builtin authentication policies for use in your
+applications. See :mod:`pyramid.authentication` for the available
+policies. They differ on their mechanisms for tracking authentication
+credentials between requests, however they all interface with your
+application in mostly the same way.
+
+Above you learned about :ref:`assigning_acls`. Each :term:`principal` used
+in the :term:`ACL` is matched against the list returned from
+:meth:`pyramid.interfaces.IAuthenticationPolicy.effective_principals`.
+Similarly, :meth:`pyramid.request.Request.authenticated_userid` maps to
+:meth:`pyramid.interfaces.IAuthenticationPolicy.authenticated_userid`.
+
+You may control these values by subclassing the default authentication
+policies. For example, below we subclass the
+:class:`pyramid.authentication.AuthTktAuthenticationPolicy` and define
+extra functionality to query our database before confirming that the
+:term:`userid` is valid in order to avoid blindly trusting the value in the
+cookie (what if the cookie is still valid but the user has deleted their
+account?). We then use that :term:`userid` to augment the
+``effective_principals`` with information about groups and other state for
+that user.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.authentication import AuthTktAuthenticationPolicy
+
+ class MyAuthenticationPolicy(AuthTktAuthenticationPolicy):
+ def authenticated_userid(self, request):
+ userid = self.unauthenticated_userid(request)
+ if userid:
+ if request.verify_userid_is_still_valid(userid):
+ return userid
+
+ def effective_principals(self, request):
+ principals = [Everyone]
+ userid = self.authenticated_userid(request)
+ if userid:
+ principals += [Authenticated, str(userid)]
+ return principals
+
+In most instances ``authenticated_userid`` and ``effective_principals`` are
+application-specific whereas ``unauthenticated_userid``, ``remember`` and
+``forget`` are generic and focused on transport/serialization of data
+between consecutive requests.
+
+.. index::
single: authentication policy (creating)
.. _creating_an_authentication_policy:
@@ -653,7 +705,7 @@ that implements the following interface:
"""
After you do so, you can pass an instance of such a class into the
-:class:`~pyramid.config.Configurator.set_authentication_policy` method
+:class:`~pyramid.config.Configurator.set_authentication_policy` method at
configuration time to use it.
.. index::
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 87a962a9a..ca6a55164 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -495,17 +495,21 @@ result in a particular view callable being invoked:
:linenos:
config.add_route('idea', 'site/{id}')
- config.add_view('mypackage.views.site_view', route_name='idea')
+ config.scan()
When a route configuration with a ``view`` attribute is added to the system,
and an incoming request matches the *pattern* of the route configuration, the
:term:`view callable` named as the ``view`` attribute of the route
configuration will be invoked.
-In the case of the above example, when the URL of a request matches
-``/site/{id}``, the view callable at the Python dotted path name
-``mypackage.views.site_view`` will be called with the request. In other
-words, we've associated a view callable directly with a route pattern.
+Recall that the ``@view_config`` is equivalent to calling ``config.add_view``,
+because the ``config.scan()`` call will import ``mypackage.views``, shown
+below, and execute ``config.add_view`` under the hood. Each view then maps the
+route name to the matching view callable. In the case of the above
+example, when the URL of a request matches ``/site/{id}``, the view callable at
+the Python dotted path name ``mypackage.views.site_view`` will be called with
+the request. In other words, we've associated a view callable directly with a
+route pattern.
When the ``/site/{id}`` route pattern matches during a request, the
``site_view`` view callable is invoked with that request as its sole
@@ -519,8 +523,10 @@ The ``mypackage.views`` module referred to above might look like so:
.. code-block:: python
:linenos:
+ from pyramid.view import view_config
from pyramid.response import Response
+ @view_config(route_name='idea')
def site_view(request):
return Response(request.matchdict['id'])
@@ -542,11 +548,30 @@ add to your application:
config.add_route('idea', 'ideas/{idea}')
config.add_route('user', 'users/{user}')
config.add_route('tag', 'tags/{tag}')
+ config.scan()
+
+Here is an example of a corresponding ``mypackage.views`` module:
- config.add_view('mypackage.views.idea_view', route_name='idea')
- config.add_view('mypackage.views.user_view', route_name='user')
- config.add_view('mypackage.views.tag_view', route_name='tag')
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import view_config
+ from pyramid.response import Response
+ @view_config(route_name='idea')
+ def idea_view(request):
+ return Response(request.matchdict['id'])
+
+ @view_config(route_name='user')
+ def user_view(request):
+ user = request.matchdict['user']
+ return Response(u'The user is {}.'.format(user))
+
+ @view_config(route_name='tag')
+ def tag_view(request):
+ tag = request.matchdict['tag']
+ return Response(u'The tag is {}.'.format(tag))
+
The above configuration will allow :app:`Pyramid` to service URLs in these
forms:
@@ -596,7 +621,7 @@ An example of using a route with a factory:
:linenos:
config.add_route('idea', 'ideas/{idea}', factory='myproject.resources.Idea')
- config.add_view('myproject.views.idea_view', route_name='idea')
+ config.scan()
The above route will manufacture an ``Idea`` resource as a :term:`context`,
assuming that ``mypackage.resources.Idea`` resolves to a class that accepts a
@@ -610,7 +635,20 @@ request in its ``__init__``. For example:
pass
In a more complicated application, this root factory might be a class
-representing a :term:`SQLAlchemy` model.
+representing a :term:`SQLAlchemy` model. The view ``mypackage.views.idea_view``
+might look like this:
+
+.. code-block:: python
+ :linenos:
+
+ @view_config(route_name='idea')
+ def idea_view(request):
+ idea = request.context
+ return Response(idea)
+
+Here, ``request.context`` is an instance of ``Idea``. If indeed the resource
+object is a SQLAlchemy model, you do not even have to perform a query in the
+view callable, since you have access to the resource via ``request.context``.
See :ref:`route_factories` for more details about how to use route factories.