diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/index.rst | 1 | ||||
| -rw-r--r-- | docs/latexindex.rst | 1 | ||||
| -rw-r--r-- | docs/narr/environment.rst | 17 | ||||
| -rw-r--r-- | docs/narr/events.rst | 62 | ||||
| -rw-r--r-- | docs/narr/handlers.rst | 277 | ||||
| -rw-r--r-- | docs/narr/i18n.rst | 10 | ||||
| -rw-r--r-- | docs/narr/security.rst | 26 | ||||
| -rw-r--r-- | docs/narr/testing.rst | 40 | ||||
| -rw-r--r-- | docs/narr/views.rst | 5 |
9 files changed, 372 insertions, 67 deletions
diff --git a/docs/index.rst b/docs/index.rst index 0dc476e46..3e70e1231 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -45,6 +45,7 @@ Narrative documentation in chapter form explaining how to use narr/renderers narr/templates narr/viewconfig + narr/handlers narr/resources narr/assets narr/webob diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 00f177e5c..2e6be2f3f 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -38,6 +38,7 @@ Narrative Documentation narr/renderers narr/templates narr/viewconfig + narr/handlers narr/resources narr/assets narr/webob diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst index 7caa1dcc6..7b7946aae 100644 --- a/docs/narr/environment.rst +++ b/docs/narr/environment.rst @@ -38,10 +38,11 @@ application-specific configuration settings. Reloading Templates ------------------- -When this value is true, reload templates without a restart, so you can see -changes to templates take effect immediately during development. This flag -is meaningful to Chameleon and Mako templates, as well as most third-party -template rendering extensions. +When this value is true, templates are automatically reloaded whenever +they are modified without restarting the application, so you can see +changes to templates take effect immediately during development. This +flag is meaningful to Chameleon and Mako templates, as well as most +third-party template rendering extensions. +---------------------------------+-----------------------------+ | Environment Variable Name | Config File Setting Name | @@ -335,6 +336,14 @@ with ``reload_``). on in one fell swoop, you can use affect settings that do not start with ``reload_*`` such as ``debug_notfound``. +.. note:: + Specifying configuration settings via environment variables is generally + most useful during development, where you may wish to augment or + override the more permanent settings in the configuration file. + This is useful because many of the reload and debug settings may + have performance or security (i.e., disclosure) implications + that make them undesirable in a production environment. + .. index:: single: reload_templates single: reload_assets diff --git a/docs/narr/events.rst b/docs/narr/events.rst index 06b30883f..90966a730 100644 --- a/docs/narr/events.rst +++ b/docs/narr/events.rst @@ -38,49 +38,51 @@ you'll need to use the need to use the :func:`pyramid.events.subscriber` decorator to decorate a function found via a :term:`scan`. -.. topic:: Configuring an Event Listener Imperatively +Configuring an Event Listener Imperatively +------------------------------------------ - You can imperatively configure a subscriber function to be called - for some event type via the - :meth:`pyramid.config.Configurator.add_subscriber` - method (see also :term:`Configurator`): +You can imperatively configure a subscriber function to be called +for some event type via the +:meth:`pyramid.config.Configurator.add_subscriber` +method (see also :term:`Configurator`): - .. code-block:: python - :linenos: +.. code-block:: python + :linenos: - from pyramid.events import NewRequest + from pyramid.events import NewRequest - from subscribers import mysubscriber + from subscribers import mysubscriber - # "config" below is assumed to be an instance of a - # pyramid.config.Configurator object + # "config" below is assumed to be an instance of a + # pyramid.config.Configurator object - config.add_subscriber(mysubscriber, NewRequest) + config.add_subscriber(mysubscriber, NewRequest) - The first argument to - :meth:`pyramid.config.Configurator.add_subscriber` is the - subscriber function (or a :term:`dotted Python name` which refers - to a subscriber callable); the second argument is the event type. +The first argument to +:meth:`pyramid.config.Configurator.add_subscriber` is the +subscriber function (or a :term:`dotted Python name` which refers +to a subscriber callable); the second argument is the event type. -.. topic:: Configuring an Event Listener Using a Decorator +Configuring an Event Listener Using a Decorator +----------------------------------------------- - You can configure a subscriber function to be called for some event - type via the :func:`pyramid.events.subscriber` function. +You can configure a subscriber function to be called for some event +type via the :func:`pyramid.events.subscriber` function. - .. code-block:: python - :linenos: +.. code-block:: python + :linenos: - from pyramid.events import NewRequest - from pyramid.events import subscriber + from pyramid.events import NewRequest + from pyramid.events import subscriber - @subscriber(NewRequest) - def mysubscriber(event): - event.request.foo = 1 + @subscriber(NewRequest) + def mysubscriber(event): + event.request.foo = 1 - When the :func:`pyramid.subscriber` decorator is used a - :term:`scan` must be performed against the package containing the - decorated function for the decorator to have any effect. See - :func:`pyramid.subscriber` for more information. +When the :func:`pyramid.subscriber` decorator is used a +:term:`scan` must be performed against the package containing the +decorated function for the decorator to have any effect. See +:func:`pyramid.subscriber` for more information. .. note:: You can also configure an event listener via ZCML. See :ref:`zcml_event_listener`. diff --git a/docs/narr/handlers.rst b/docs/narr/handlers.rst new file mode 100644 index 000000000..edeed2de8 --- /dev/null +++ b/docs/narr/handlers.rst @@ -0,0 +1,277 @@ +.. _handlers_chapter: + +View Handlers +============= + +:app:`Pyramid` provides the special concept of a :term:`view handler`. View +handlers are view classes that implement a number of methods, each of which +is a :term:`view callable` as a convenience for :term:`URL dispatch` users. + +.. note:: + + View handlers are *not* useful when using :term:`traversal`, only when using + :term:`url dispatch`. If you are not using url dispatch, you can skip this + chapter. + +Using a view handler instead of a plain function or class :term:`view +callable` makes it unnecessary to call +:meth:`pyramid.config.Configurator.add_route` (and/or +:meth:`pyramid.config.Configurator.add_view`) "by hand" multiple times, +making it more pleasant to register a collection of views as a single class +when using :term:`url dispatch`. The view handler machinery also introduces +the concept of an ``action``, which is used as a :term:`view predicate` to +control which method of the handler is called. The method name is the +default *action name* of a handler view callable. + +The concept of a view handler is analogous to a "controller" in Pylons 1.0. + +The view handler class is initialized by :app:`Pyramid` in the same manner as +a "plain" view class. Its ``__init__`` is called with a request object (see +:ref:`class_as_view`). It implements methods, each of which is a :term:`view +callable`. When a request enters the system which corresponds with an +*action* related to one of its view callable methods, this method is called, +and it is expected to return a response. + +Here's an example view handler class: + +.. code-block:: python + :linenos: + + from pyramid.response import Response + + from pyramid.view import action + + class Hello(object): + def __init__(self, request): + self.request = request + + def index(self): + return Response('Hello world!') + + @action(renderer="mytemplate.mak") + def bye(self): + return {} + +The :class:`pyramid.view.action` decorator is used to fine-tune the view +parameters for each potential view callable which is a method of the handler. + +.. _using_add_handler: + +Handler Registration Using :meth:`~pyramid.config.Configurator.add_handler` +--------------------------------------------------------------------------- + +Handlers are added to application configuration via the +:meth:`pyramid.config.Configurator.add_handler` API. The +:meth:`~pyramid.config.Configurator.add_handler` method will scan a +:term:`view handler` class and automatically set up view configurations for +its methods that represent "auto-exposed" view callable, or those that were +decorated explicitly with the :class:`~pyramid.view.action` decorator. This +decorator is used to setup additional view configuration information for +individual methods of the class, and can be used repeatedly for a single view +method to register multiple view configurations for it. + +.. code-block:: python + :linenos: + + from myapp.handlers import Hello + config.add_handler('hello', '/hello/{action}', handler=Hello) + +This example will result in a route being added for the pattern +``/hello/{action}``, and each method of the ``Hello`` class will then be +examined to see if it should be registered as a potential view callable when +the ``/hello/{action}`` pattern matches. The value of ``{action}`` in the +route pattern will be used to determine which view should be called, and each +view in the class will be setup with a view predicate that requires a +specific ``action`` name. By default, the action name for a method of a +handler is the method name. + +If the URL was ``/hello/index``, the above example pattern would match, and, +by default, the ``index`` method of the ``Hello`` class would be called. + +Alternatively, the action can be declared specifically for a URL to be +registered for a *specific* ``action`` name: + +.. code-block:: python + :linenos: + + from myapp.handlers import Hello + config.add_handler('hello_index', '/hello/index', + handler=Hello, action='index') + +This will result one of the methods that are configured for the ``action`` of +'index' in the ``Hello`` handler class to be called. In this case the name of +the method is the same as the action name: ``index``. However, this need not +be the case, as we will see below. + +When calling :meth:`~pyramid.config.Configurator.add_handler`, an ``action`` +is required in either the route pattern or as a keyword argument, but +**cannot appear in both places**. A ``handler`` argument must also be +supplied, which can be either a :term:`asset specification` or a Python +reference to the handler class. Additional keyword arguments are passed +directly through to :meth:`pyramid.config.Configurator.add_route`. + +For example: + +.. code-block:: python + :linenos: + + config.add_handler('hello', '/hello/{action}', + handler='mypackage.handlers.MyHandler') + +Multiple :meth:`~pyramid.config.Configurator.add_handler` calls can specify +the same handler, to register specific route names for different +handler/action combinations. For example: + +.. code-block:: python + :linenos: + + config.add_handler('hello_index', '/hello/index', + handler=Hello, action='index') + config.add_handler('bye_index', '/hello/bye', + handler=Hello, action='bye') + +.. note:: + + Handler configuration may also be added to the system via :term:`ZCML` (see + :ref:`zcml_handler_configuration`). + +View Setup in the Handler Class +------------------------------- + +A handler class can have a single class level attribute called +``__autoexpose__`` which should be a regular expression or the value +``None``. It's used to determine which method names will result in additional +view configurations being registered. + +When :meth:`~pyramid.config.Configurator.add_handler` runs, every method in +the handler class will be searched and a view registered if the method name +matches the ``__autoexpose__`` regular expression, or if the method was +decorated with :class:`~pyramid.view.action`. + +Every method in the handler class that has a name meeting the +``__autoexpose__`` regular expression will have a view registered for an +``action`` name corresponding to the method name. This functionality can be +disabled by setting the ``__autoexpose__`` attribute to ``None``: + +.. code-block:: python + :linenos: + + from pyramid.view import action + + class Hello(object): + __autoexpose__ = None + + def __init__(self, request): + self.request = request + + @action() + def index(self): + return Response('Hello world!') + + @action(renderer="mytemplate.mak") + def bye(self): + return {} + +With auto-expose effectively disabled, no views will be registered for a +method unless it is specifically decorated with +:class:`~pyramid.view.action`. + +Action Decorators in a Handler +------------------------------ + +The :class:`~pyramid.view.action` decorator registers view configuration +information on the handler method, which is used by +:meth:`~pyramid.config.Configurator.add_handler` to setup the view +configuration. + +All keyword arguments are recorded, and passed to +:meth:`~pyramid.config.Configurator.add_view`. Any valid keyword arguments +for :meth:`~pyramid.config.Configurator.add_view` can thus be used with the +:class:`~pyramid.view.action` decorator to further restrict when the view +will be called. + +One important difference is that a handler method can respond to an +``action`` name that is different from the method name by passing in a +``name`` argument. + +Example: + +.. code-block:: python + :linenos: + + from pyramid.view import action + + class Hello(object): + def __init__(self, request): + self.request = request + + @action(name='index', renderer='created.mak', request_method='POST') + def create(self): + return {} + + @action(renderer="view_all.mak", request_method='GET') + def index(self): + return {} + +This will register two views that require the ``action`` to be ``index``, +with the additional view predicate requiring a specific request method. + +It can be useful to decorate a single method multiple times with +:class:`~pyramid.view.action`. Each action decorator will register a new view +for the method. By specifying different names and renderers for each action, +the same view logic can be exposed and rendered differently on multiple URLs. + +Example: + +.. code-block:: python + :linenos: + + from pyramid.view import action + + class Hello(object): + def __init__(self, request): + self.request = request + + @action(name='home', renderer='home.mak') + @action(name='about', renderer='about.mak') + def show_template(self): + # prep some template vars + return {} + + # in the config + config.add_handler('hello', '/hello/{action}', handler=Hello) + +With this configuration, the url ``/hello/home`` will find a view +configuration that results in calling the ``show_template`` method, then +rendering the template with ``home.mak``, and the url ``/hello/about`` will +call the same method and render the ``about.mak`` template. + +Handler ``__action_decorator__`` Attribute +------------------------------------------ + +If a handler class has an ``__action_decorator__`` attribute, then the +value of the class attribute will be passed in as the ``decorator`` +argument every time a handler action is registered as a view callable. +This means that, like anything passed to ``add_view()`` as the +``decorator`` argument, ``__action_decorator__`` must be a callable +accepting a single argument. This argument will itself be a callable +accepting ``(context, request)`` arguments, and +``__action_decorator__`` must return a replacement callable with the +same call signature. + +Note that, since handler actions are registered as views against the +handler class and not a handler instance, any ``__action_decorator__`` +attribute must *not* be a regular instance method. Defining an +``__action_decorator__`` instance method on a handler class will +result in a :exc:`ConfigurationError`. Instead, ``__action_decorator__`` +can be any other type of callable: a staticmethod, classmethod, function, +or some sort of callable instance. + +.. note:: + + In a Pylons 1.0 controller, it was possible to override the ``__call__()`` + method, which allowed a developer to "wrap" the entire action invocation, + with a try/except or any other arbitrary code. In :app:`Pyramid`, this + can be emulated with the use of an ``__action_decorator__`` classmethod + on your handler class. + diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst index c2a5b8ce7..c7ed18e4c 100644 --- a/docs/narr/i18n.rst +++ b/docs/narr/i18n.rst @@ -867,11 +867,11 @@ which itself includes an ``LC_MESSAGES`` directory. Each Each ``.mo`` file represents a :term:`message catalog`, which is used to provide translations to your application. -Adding a :term:`translation directory` registers all of its -constituent :term:`message catalog` files (all of the ``.mo`` files -found within all ``LC_MESSAGES`` directories within each locale -directory in the translation directory) within your :app:`Pyramid` -application to be available to use for translation services. +Adding a :term:`translation directory` registers all of its constituent +:term:`message catalog` files within your :app:`Pyramid` application to +be available to use for translation services. This includes all of the +``.mo`` files found within all ``LC_MESSAGES`` directories within each +locale directory in the translation directory. You can add a translation directory imperatively by using the :meth:`pyramid.config.Configurator.add_translation_dirs` during diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 62a4727bc..babf66ecd 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -6,13 +6,14 @@ Security ======== -:app:`Pyramid` provides an optional declarative authorization system that -prevents a :term:`view` from being invoked when the user represented by -credentials in the :term:`request` does not have an appropriate level of -access when a particular resource is the :term:`context`. Here's how it -works at a high level: +:app:`Pyramid` provides an optional declarative authorization system +that can prevent a :term:`view` from being invoked based on an +:term:`authorization policy`. Before a view is invoked, the +authorization system can use the credentials in the :term:`request` +along with the :term:`context` resource to determine if access will be +allowed. Here's how it works at a high level: -- A :term:`request` is generated when a user visits our application. +- A :term:`request` is generated when a user visits the application. - Based on the request, a :term:`context` resource is located through :term:`resource location`. A context is located differently depending on @@ -40,6 +41,15 @@ works at a high level: - If the authorization policy denies access, the view callable is not invoked; instead the :term:`forbidden view` is invoked. +Security in :app:`Pyramid`, unlike many systems, cleanly and explicitly +separates authentication and authorization. Authentication is merely the +mechanism by which credentials provided in the :term:`request` are +resolved to one or more :term:`principal` identifiers. These identifiers +represent the users and groups in effect during the request. +Authorization then determines access based on the :term:`principal` +identifiers, the :term:`view callable` being invoked, and the +:term:`context` resource. + Authorization is enabled by modifying your application to include an :term:`authentication policy` and :term:`authorization policy`. :app:`Pyramid` comes with a variety of implementations of these @@ -169,8 +179,8 @@ to invoke the ``blog_entry_add_view`` view. If he does not, the Setting a Default Permission ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If a permission is not supplied to a view configuration, the -registered view always be executable by entirely anonymous users: any +If a permission is not supplied to a view configuration, the registered +view will always be executable by entirely anonymous users: any authorization policy in effect is ignored. In support of making it easier to configure applications which are diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index 08c6e355b..1428f3476 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -87,14 +87,15 @@ local` stack, which makes the ``get_current_*`` functions work. It returns a required by the code under test. :func:`~pyramid.testing.tearDown` pops the thread local stack. -Normally when a Configurator is used directly with the ``main`` block of a -Pyramid application, it defers performing any "real work" until its +Normally when a Configurator is used directly with the ``main`` block of +a Pyramid application, it defers performing any "real work" until its ``.commit`` method is called (often implicitly by the -:meth:`pyramid.config.Configurator.make_wsgi_app` method). The Configurator -returned by :func:`~pyramid.testing.setUp` is however an *autocommitting* -Configurator which performs all actions implied by methods called on it -immediately. This is more convenient for unit-testing purposes than needing -to call :meth:`pyramid.config.Configurator.commit` in each test after adding +:meth:`pyramid.config.Configurator.make_wsgi_app` method). The +Configurator returned by :func:`~pyramid.testing.setUp` is an +*autocommitting* Configurator, however, which performs all actions +implied by methods called on it immediately. This is more convenient +for unit-testing purposes than needing to call +:meth:`pyramid.config.Configurator.commit` in each test after adding extra configuration statements. The use of the :func:`~pyramid.testing.setUp` and @@ -117,11 +118,10 @@ of using this feature: testing.tearDown() The above will make sure that -:func:`pyramid.threadlocal.get_current_registry` will return the -:term:`application registry` associated with the ``config`` Configurator -instance when :func:`pyramid.threadlocal.get_current_registry` is called in a -test case method attached to ``MyTest``. Each test case method attached to -``MyTest`` will use an isolated registry. +:func:`pyramid.threadlocal.get_current_registry` called within a test +case method of ``MyTest`` will return the :term:`application registry` +associated with the ``config`` Configurator instance. Each test case +method attached to ``MyTest`` will use an isolated registry. The :func:`~pyramid.testing.setUp` and :func:`~pyramid.testing.tearDown` functions accepts various arguments that influence the environment of the @@ -148,14 +148,14 @@ other than ``None`` during the course of a single test, you can pass a testing.tearDown() If you pass a :term:`request` object into :func:`pyramid.testing.setUp` -within your test case's ``setUp``, any test method attached to the ``MyTest`` -test case that directly or indirectly calls -:func:`pyramid.threadlocal.get_current_request` will receive the request you -passed into the ``begin`` method. Otherwise, during testing, -:func:`pyramid.threadlocal.get_current_request` will return ``None``. We use -a "dummy" request implementation supplied by -:class:`pyramid.testing.DummyRequest` because it's easier to construct than a -"real" :app:`Pyramid` request object. +within your test case's ``setUp``, any test method attached to the +``MyTest`` test case that directly or indirectly calls +:func:`pyramid.threadlocal.get_current_request` will receive the request +object. Otherwise, during testing, +:func:`pyramid.threadlocal.get_current_request` will return ``None``. +We use a "dummy" request implementation supplied by +:class:`pyramid.testing.DummyRequest` because it's easier to construct +than a "real" :app:`Pyramid` request object. What? ~~~~~ diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 6ab8e9e45..3e0451383 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -135,6 +135,11 @@ method expected to return a response, you can either: kind of view class which provides more automation when your application uses :term:`URL dispatch` solely. +.. note:: + You can also create :term:`view handler` classes that define + multiple view callables similar to "controllers" in Pylons 1.0. See + the :ref:`handlers_chapter` chapter for details. + .. index:: single: view calling convention |
