summaryrefslogtreecommitdiff
path: root/docs/narr
diff options
context:
space:
mode:
authorCasey Duncan <casey.duncan@gmail.com>2010-12-27 09:28:50 -0700
committerCasey Duncan <casey.duncan@gmail.com>2010-12-27 09:28:50 -0700
commit6f7b6c0221f8c120a22b630eea26905744d2b6b4 (patch)
tree6c8e1ae3affba81ec0c26eb183c4f424b577a1d5 /docs/narr
parent6d0ff23fc3094f3f40d93d020bbc80ecdfceb825 (diff)
parente8db031e8cd22affb65254539ae210f64d37f36e (diff)
downloadpyramid-6f7b6c0221f8c120a22b630eea26905744d2b6b4.tar.gz
pyramid-6f7b6c0221f8c120a22b630eea26905744d2b6b4.tar.bz2
pyramid-6f7b6c0221f8c120a22b630eea26905744d2b6b4.zip
Merge https://github.com/Pylons/pyramid
Diffstat (limited to 'docs/narr')
-rw-r--r--docs/narr/advconfig.rst395
-rw-r--r--docs/narr/assets.rst397
-rw-r--r--docs/narr/csrf.rst63
-rw-r--r--docs/narr/declarative.rst220
-rw-r--r--docs/narr/extending.rst421
-rw-r--r--docs/narr/flash.rst107
-rw-r--r--docs/narr/hooks.rst668
-rw-r--r--docs/narr/i18n.rst5
-rw-r--r--docs/narr/project.rst3
-rw-r--r--docs/narr/resources.rst5
-rw-r--r--docs/narr/sessions.rst4
-rw-r--r--docs/narr/static.rst277
-rw-r--r--docs/narr/views.rst2
13 files changed, 1668 insertions, 899 deletions
diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst
new file mode 100644
index 000000000..f8b3ee191
--- /dev/null
+++ b/docs/narr/advconfig.rst
@@ -0,0 +1,395 @@
+.. index::
+ pair: advanced; configuration
+
+.. _advconfig_narr:
+
+Advanced Configuration
+======================
+
+To support application extensibility, the :app:`Pyramid`
+:term:`Configurator`, by default, detects configuration conflicts and allows
+you to include configuration imperatively from other packages or modules. It
+also, by default, performs configuration in two separate phases. This allows
+you to ignore relative configuration statement ordering in some
+circumstances.
+
+.. index::
+ single: imperative configuration
+
+.. _conflict_detection:
+
+Conflict Detection
+------------------
+
+Here's a familiar example of one of the simplest :app:`Pyramid` applications,
+configured imperatively:
+
+.. code-block:: python
+ :linenos:
+
+ from paste.httpserver import serve
+ from pyramid.config import Configurator
+ from pyramid.response import Response
+
+ def hello_world(request):
+ return Response('Hello world!')
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.add_view(hello_world)
+ app = config.make_wsgi_app()
+ serve(app, host='0.0.0.0')
+
+When you start this application, all will be OK. However, what happens if we
+try to add another view to the configuration with the same set of
+:term:`predicate` arguments as one we've already added?
+
+.. code-block:: python
+ :linenos:
+
+ from paste.httpserver import serve
+ from pyramid.config import Configurator
+ from pyramid.response import Response
+
+ def hello_world(request):
+ return Response('Hello world!')
+
+ def goodbye_world(request):
+ return Response('Goodbye world!')
+
+ if __name__ == '__main__':
+ config = Configurator()
+
+ config.add_view(hello_world, name='hello')
+
+ # conflicting view configuration
+ config.add_view(goodbye_world, name='hello')
+
+ app = config.make_wsgi_app()
+ serve(app, host='0.0.0.0')
+
+The application now has two conflicting view configuration statements. When
+we try to start it again, it won't start. Instead, we'll receive a traceback
+that ends something like this:
+
+.. code-block:: guess
+ :linenos:
+
+ Traceback (most recent call last):
+ File "app.py", line 12, in <module>
+ app = config.make_wsgi_app()
+ File "pyramid/config.py", line 839, in make_wsgi_app
+ self.commit()
+ File "pyramid/pyramid/config.py", line 473, in commit
+ self._ctx.execute_actions()
+ File "zope/configuration/config.py", line 600, in execute_actions
+ for action in resolveConflicts(self.actions):
+ File "zope/configuration/config.py", line 1507, in resolveConflicts
+ raise ConfigurationConflictError(conflicts)
+ zope.configuration.config.ConfigurationConflictError:
+ Conflicting configuration actions
+ For: ('view', None, '', None, <InterfaceClass pyramid.interfaces.IView>,
+ None, None, None, None, None, False, None, None, None)
+ ('app.py', 14, '<module>', 'config.add_view(hello_world)')
+ ('app.py', 17, '<module>', 'config.add_view(hello_world)')
+
+This traceback is trying to tell us:
+
+- We've got conflicting information for a set of view configuration
+ statements (The ``For:`` line).
+
+- There are two statements which conflict, shown beneath the ``For:`` line:
+ ``config.add_view(hello_world. 'hello')`` on line 14 of ``app.py``, and
+ ``config.add_view(goodbye_world, 'hello')`` on line 17 of ``app.py``.
+
+These two configuration statements are in conflict because we've tried to
+tell the system that the set of :term:`predicate` values for both view
+configurations are exactly the same. Both the ``hello_world`` and
+``goodbye_world`` views are configured to respond under the same set of
+circumstances. This circumstance: the :term:`view name` (represented by the
+``name=`` predicate) is ``hello``.
+
+This presents an ambiguity that :app:`Pyramid` cannot resolve. Rather than
+allowing the circumstance to go unreported, by default Pyramid raises a
+:exc:`ConfigurationConflictError` error and prevents the application from
+running.
+
+Conflict detection happens for any kind of configuration: imperative
+configuration, :term:`ZCML` configuration, or configuration that results from
+the execution of a :term:`scan`.
+
+.. note:: If you use, ZCML, its conflict detection algorithm is described in
+ :ref:`zcml_conflict_detection`.
+
+Manually Resolving Conflicts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a number of ways to manually resolve conflicts: the "right" way, by
+strategically using :meth:`pyramid.config.Configurator.commit`, or by using
+an "autocommitting" configurator.
+
+The Right Thing
++++++++++++++++
+
+The most correct way to resolve conflicts is to "do the needful": change your
+configuration code to not have conflicting configuration statements. The
+details of how this is done depends entirely on the configuration statements
+made by your application. Use the detail provided in the
+:exc:`ConfigurationConflictError` to track down the offending conflicts and
+modify your configuration code accordingly.
+
+If you're getting a conflict while trying to extend an existing application,
+and that application has a function which performs configuration like this
+one:
+
+.. code-block:: python
+ :linenos:
+
+ def add_routes(config):
+ config.add_route(...)
+
+Don't call this function directly with ``config`` as an argument. Instead,
+use :meth:`pyramid.config.Configuration.include`:
+
+.. code-block:: python
+ :linenos:
+
+ config.include(add_routes)
+
+Using :meth:`~pyramid.config.Configuration.include` instead of calling the
+function directly provides a modicum of automated conflict resolution, with
+the configuration statements you define in the calling code overriding those
+of the included function. See also :ref:`automatic_conflict_resolution` and
+:ref:`including_configuration`.
+
+Using ``config.commit()``
++++++++++++++++++++++++++
+
+You can manually commit a configuration by using the
+:meth:`pyramid.config.Configurator.commit` method between configuration
+calls. For example, we prevent conflicts from occurring in the application
+we examined previously as the result of adding a ``commit``. Here's the
+application that generates conflicts:
+
+.. code-block:: python
+ :linenos:
+
+ from paste.httpserver import serve
+ from pyramid.config import Configurator
+ from pyramid.response import Response
+
+ def hello_world(request):
+ return Response('Hello world!')
+
+ def goodbye_world(request):
+ return Response('Goodbye world!')
+
+ if __name__ == '__main__':
+ config = Configurator()
+
+ config.add_view(hello_world, name='hello')
+
+ # conflicting view configuration
+ config.add_view(goodbye_world, name='hello')
+
+ app = config.make_wsgi_app()
+ serve(app, host='0.0.0.0')
+
+We can prevent the two ``add_view`` calls from conflicting by issuing a call
+to :meth:`~pyramid.config.Configurator.commit` between them:
+
+.. code-block:: python
+ :linenos:
+
+ from paste.httpserver import serve
+ from pyramid.config import Configurator
+ from pyramid.response import Response
+
+ def hello_world(request):
+ return Response('Hello world!')
+
+ def goodbye_world(request):
+ return Response('Goodbye world!')
+
+ if __name__ == '__main__':
+ config = Configurator()
+
+ config.add_view(hello_world, name='hello')
+
+ config.commit() # commit any pending configuration actions
+
+ # no-longer-conflicting view configuration
+ config.add_view(goodbye_world, name='hello')
+
+ app = config.make_wsgi_app()
+ serve(app, host='0.0.0.0')
+
+In the above example we've issued a call to
+:meth:`~pyramid.config.Configurator.commit` between the two ``add_view``
+calls. :meth:`~pyramid.config.Configurator.commit` will cause any pending
+configuration statements.
+
+Calling :meth:`~pyramid.config.Configurator.commit` is safe at any time. It
+executes all pending configuration actions and leaves the configuration
+action list "clean".
+
+Note that :meth:`~pyramid.config.Configurator.commit` has no effect when
+you're using an *autocommitting* configurator (see
+:ref:`autocommitting_configurator`).
+
+.. _autocommitting_configurator:
+
+Using An Autocommitting Configurator
+++++++++++++++++++++++++++++++++++++
+
+You can also use a heavy hammer to circumvent conflict detection by using a
+configurator constructor parameter: ``autocommit=True``. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.config import Configurator
+
+ if __name__ == '__main__':
+ config = Configurator(autocommit=True)
+
+When the ``autocommit`` parameter passed to the Configurator is ``True``,
+conflict detection (and :ref:`twophase_config`) is disabled. Configuration
+statements will be executed immediately, and succeeding statements will
+override preceding ones.
+
+:meth:`pyramid.config.Configurator.commit` has no effect when ``autocommit``
+is ``True``.
+
+If you use a Configurator in code that performs unit testing, it's usually a
+good idea to use an autocommitting Configurator, because you are usually
+unconcerned about conflict detection or two-phase configuration in test code.
+
+.. _automatic_conflict_resolution:
+
+Automatic Conflict Resolution
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If your code uses the :meth:`pyramid.config.Configurator.include` method to
+include external configuration, some conflicts are automatically resolved.
+Configuration statements that are made as the result of an "include" will be
+overridden by configuration statements that happen within the caller of
+the "include" method. See also
+
+Automatic conflict resolution supports this goal: if a user wants to reuse a
+Pyramid application, and they want to customize the configuration of this
+application without hacking its code "from outside", they can "include" a
+configuration function from the package and override only some of its
+configuration statements within the code that does the include. No conflicts
+will be generated by configuration statements within the code which does the
+including, even if configuration statements in the included code would
+conflict if it was moved "up" to the calling code.
+
+Methods Which Provide Conflict Detection
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These are the methods of the configurator which provide conflict detection:
+
+:meth:`~pyramid.config.Configurator.add_view`,
+:meth:`~pyramid.config.Configurator.add_route`,
+:meth:`~pyramid.config.Configurator.add_renderer`,
+:meth:`~pyramid.config.Configurator.set_request_factory`,
+:meth:`~pyramid.config.Configurator.set_renderer_globals_factory`
+:meth:`~pyramid.config.Configurator.set_locale_negotiator` and
+:meth:`~pyramid.config.Configurator.set_default_permission`.
+
+Some other methods of the configurator also indirectly provide conflict
+detection, because they're implemented in terms of conflict-aware methods:
+
+- :meth:`~pyramid.config.Configurator.add_handler`, a frontend for
+ ``add_route`` and ``add_view``.
+
+- :meth:`~pyramid.config.Configurator.add_route` does a second type of
+ conflict detection when a ``view`` parameter is passed (it calls
+ ``add_view``).
+
+- :meth:`~pyramid.config.Configurator.static_view`, a frontend for
+ ``add_route`` and ``add_view``.
+
+.. _including_configuration:
+
+Including Configuration from External Sources
+---------------------------------------------
+
+Some application programmers will factor their configuration code in such a
+way that it is easy to reuse and override configuration statements. For
+example, such a developer might factor out a function used to add routes to
+his application:
+
+.. code-block:: python
+ :linenos:
+
+ def add_routes(config):
+ config.add_route(...)
+
+Rather than calling this function directly with ``config`` as an argument.
+Instead, use :meth:`pyramid.config.Configuration.include`:
+
+.. code-block:: python
+ :linenos:
+
+ config.include(add_routes)
+
+Using ``include`` rather than calling the function directly will allow
+:ref:`automatic_conflict_resolution` to work.
+
+.. note: See :ref:`the_include_tag` for a declarative alternative to
+ :meth:`pyramid.config.Configurator.include`.
+
+.. _twophase_config:
+
+Two-Phase Configuration
+-----------------------
+
+When a non-autocommitting :term:`Configurator` is used to do configuration
+(the default), configuration execution happens in two phases. In the first
+phase, "eager" configuration actions (actions that must happen before all
+others, such as registering a renderer) are executed, and *discriminators*
+are computed for each of the actions that depend on the result of the eager
+actions. In the second phase, the discriminators of all actions are compared
+to do conflict detection.
+
+Due to this, for configuration methods that have no internal ordering
+constraints, execution order of configuration method calls is not important.
+For example, the relative ordering of
+:meth:`pyramid.config.Configurator.add_view` and
+:meth:`pyramid.config.Configurator.add_renderer` is unimportant when a
+non-autocommitting configurator is used. This code snippet:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_view('some.view', renderer='path_to_custom/renderer.rn')
+ config.add_renderer('.rn', SomeCustomRendererFactory)
+
+Has the same result as:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_renderer('.rn', SomeCustomRendererFactory)
+ config.add_view('some.view', renderer='path_to_custom/renderer.rn')
+
+Even though the view statement depends on the registration of a custom
+renderer, due to two-phase configuration, the order in which the
+configuration statements are issued is not important. ``add_view`` will be
+able to find the ``.rn`` renderer even if ``add_renderer`` is called after
+``add_view``.
+
+The same is untrue when you use an *autocommitting* configurator (see
+:ref:`autocommitting_configurator`). When an autocommitting configurator is
+used, two-phase configuration is disabled, and configuration statements must
+be ordered in dependency order.
+
+Some configuration methods, such as
+:meth:`pyramid.config.Configurator.add_route` and
+:meth:`pyramid.config.Configurator.add_handler` have internal ordering
+constraints: they routes they imply require relative ordering. Such ordering
+constraints are not absolved by two-phase configuration. Routes are still
+added in configuration execution order.
+
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index 1932e19ff..f147426ce 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -1,74 +1,403 @@
.. index::
single: assets
+ single: static asssets
.. _assets_chapter:
-Assets
-======
+Static Assets
+=============
An :term:`asset` is any file contained within a Python :term:`package` which
is *not* a Python source code file. For example, each of the following is an
asset:
-- a :term:`Chameleon` template file contained within a Python package.
+- a GIF image file contained within a Python package or contained within any
+ subdirectory of a Python package.
-- a GIF image file contained within a Python package.
+- a CSS file contained within a Python package or contained within any
+ subdirectory of a Python package.
-- a CSS file contained within a Python package.
-
-- a JavaScript source file contained within a Python package.
+- a JavaScript source file contained within a Python package or contained
+ within any subdirectory of a Python package.
- A directory within a package that does not have an ``__init__.py``
in it (if it possessed an ``__init__.py`` it would *be* a package).
+- a :term:`Chameleon` or :term:`Mako` template file contained within a Python
+ package.
+
The use of assets is quite common in most web development projects. For
example, when you create a :app:`Pyramid` application using one of the
available "paster" templates, as described in :ref:`creating_a_project`, the
directory representing the application contains a Python :term:`package`.
Within that Python package, there are directories full of files which are
-assets. For example, there is a ``templates`` directory which contains
-``.pt`` files, and a ``static`` directory which contains ``.css``, ``.js``,
-and ``.gif`` files.
+static assets. For example, there's a ``static`` directory which contains
+``.css``, ``.js``, and ``.gif`` files. These asset files are delivered when
+a user visits an application URL.
-.. _understanding_assets:
+.. _asset_specifications:
-Understanding Assets
---------------------
+Understanding Asset Specifications
+----------------------------------
Let's imagine you've created a :app:`Pyramid` application that uses a
:term:`Chameleon` ZPT template via the
-:func:`pyramid.chameleon_zpt.render_template_to_response` API. For example,
-the application might address the asset named ``templates/some_template.pt``
-using that API within a ``views.py`` file inside a ``myapp`` package:
+:func:`pyramid.renderers.render_to_response` API. For example, the
+application might address the asset using the :term:`asset specification`
+``myapp:templates/some_template.pt`` using that API within a ``views.py``
+file inside a ``myapp`` package:
.. ignore-next-block
.. code-block:: python
:linenos:
- from pyramid.chameleon_zpt import render_template_to_response
- render_template_to_response('templates/some_template.pt')
+ from pyramid.renderers import render_to_response
+ render_to_response('myapp:templates/some_template.pt', {}, request)
-"Under the hood", when this API is called, :app:`Pyramid` attempts
-to make sense out of the string ``templates/some_template.pt``
-provided by the developer. To do so, it first finds the "current"
-package. The "current" package is the Python package in which the
-``views.py`` module which contains this code lives. This would be the
-``myapp`` package, according to our example so far. By resolving the
-current package, :app:`Pyramid` has enough information to locate
-the actual template file. These are the elements it needs:
+"Under the hood", when this API is called, :app:`Pyramid` attempts to make
+sense out of the string ``myapp:templates/some_template.pt`` provided by the
+developer. This string is an :term:`asset specification`. It is composed of
+two parts:
- The *package name* (``myapp``)
-- The *asset name* (``templates/some_template.pt``)
+- The *asset name* (``templates/some_template.pt``), relative to the package
+ directory.
+
+The two parts are separated by the colon character.
+
+:app:`Pyramid` uses the Python :term:`pkg_resources` API to resolve the
+package name and asset name to an absolute (operating-system-specific) file
+name. It eventually passes this resolved absolute filesystem path to the
+Chameleon templating engine, which then uses it to load, parse, and execute
+the template file.
+
+There is a second form of asset specification: a *relative* asset
+specification. Instead of using an "absolute" asset specification which
+includes the package name, in certain circumstances you can omit the package
+name from the specification. For example, you might be able to use
+``templates/mytemplate.pt`` instead of ``myapp:templates/some_template.pt``.
+Such asset specifications are usually relative to a "current package." The
+"current package" is usually the package which contains the code that *uses*
+the asset specification. :app:`Pyramid` APIs which accept relative asset
+specifications typically describe what the asset is relative to in their
+individual documentation.
+
+.. index::
+ single: add_static_view
+
+.. _static_assets_section:
+
+Serving Static Assets
+---------------------
+
+:app:`Pyramid` makes it possible to serve up static asset files from a
+directory on a filesystem to an application user's browser. Use the
+:meth:`pyramid.config.Configurator.add_static_view` to instruct
+:app:`Pyramid` to serve static assets such as JavaScript and CSS files. This
+mechanism makes a directory of static files available at a name relative to
+the application root URL, e.g. ``/static`` or as an external URL.
+
+.. note:: `~pyramid.config.Configurator.add_static_view` cannot serve a
+ single file, nor can it serve a directory of static files directly
+ relative to the root URL of a :app:`Pyramid` application. For these
+ features, see :ref:`advanced_static`.
+
+Here's an example of a use of
+:meth:`~pyramid.config.Configurator.add_static_view` that will serve files up
+from the ``/var/www/static`` directory of the computer which runs the
+:app:`Pyramid` application as URLs beneath the ``/static`` URL prefix.
+
+.. code-block:: python
+ :linenos:
+
+ # config is an instance of pyramid.config.Configurator
+ config.add_static_view(name='static', path='/var/www/static')
+
+The ``name`` prepresents a URL *prefix*. In order for files that live in the
+``path`` directory to be served, a URL that requests one of them must begin
+with that prefix. In the example above, ``name`` is ``static``, and ``path``
+is ``/var/www/static``. In English, this means that you wish to serve the
+files that live in ``/var/www/static`` as sub-URLs of the ``/static`` URL
+prefix. Therefore, the file ``/var/www/static/foo.css`` will be returned
+when the user visits your application's URL ``/static/foo.css``.
+
+A static directory named at ``path`` may contain subdirectories recursively,
+and any subdirectories may hold files; these will be resolved by the static
+view as you would expect. The ``Content-Type`` header returned by the static
+view for each particular type of file is dependent upon its file extension.
+
+By default, all files made available via
+:meth:`~pyramid.config.Configurator.add_static_view` are accessible by
+completely anonymous users. Simple authorization can be required, however.
+To protect a set of static files using a permission, in addition to passing
+the required ``name`` and ``path`` arguments, also pass the ``permission``
+keyword argument to :meth:`~pyramid.config.Configurator.add_static_view`.
+The value of the ``permission`` argument represents the :term:`permission`
+that the user must have relative to the current :term:`context` when the
+static view is invoked. A user will be required to possess this permission
+to view any of the files represented by ``path`` of the static view. If your
+static resources must be protected by a more complex authorization scheme,
+see :ref:`advanced_static`.
+
+Here's another example that uses an :term:`asset specification` instead of an
+absolute path as the ``path`` argument. To convince
+:meth:`pyramid.config.Configurator.add_static_view` to serve files up under
+the ``/static`` URL from the ``a/b/c/static`` directory of the Python package
+named ``some_package``, we can use a fully qualified :term:`asset
+specification` as the ``path``:
+
+.. code-block:: python
+ :linenos:
+
+ # config is an instance of pyramid.config.Configurator
+ config.add_static_view(name='static', path='some_package:a/b/c/static')
+
+The ``path`` provided to :meth:`pyramid.config.Configurator.add_static_view`
+may be a fully qualified :term:`asset specification` or an *absolute path*.
+
+Instead of representing a URL prefix, the ``name`` argument of a call to
+:meth:`pyramid.config.Configurator.add_static_view` can alternately be a
+*URL*. Each of examples we've seen so far have shown usage of the ``name``
+argument as a URL prefix. However, when ``name`` is a *URL*, static assets
+can be served from an external webserver. In this mode, the ``name`` is used
+as the URL prefix when generating a URL using :func:`pyramid.url.static_url`.
+
+For example, :meth:`pyramid.config.Configurator.add_static_view` may
+be fed a ``name`` argument which is ``http://example.com/images``:
+
+.. code-block:: python
+ :linenos:
+
+ # config is an instance of pyramid.config.Configurator
+ config.add_static_view(name='http://example.com/images',
+ path='mypackage:images')
+
+Because :meth:`pyramid.config.Configurator.add_static_view` is provided with
+a ``name`` argument that is the URL ``http://example.com/images``, subsequent
+calls to :func:`pyramid.url.static_url` with paths that start with the
+``path`` argument passed to
+:meth:`pyramid.config.Configurator.add_static_view` will generate a URL
+something like ``http://example.com/images/logo.png``. The external
+webserver listening on ``example.com`` must be itself configured to respond
+properly to such a request. The :func:`pyramid.url.static_url` API is
+discussed in more detail later in this chapter.
+
+.. note::
+
+ The :ref:`static_directive` ZCML directive offers an declarative
+ equivalent to :meth:`pyramid.config.Configurator.add_static_view`. Use of
+ the :ref:`static_directive` ZCML directive is completely equivalent to
+ using imperative configuration for the same purpose.
+
+.. index::
+ single: generating static asset urls
+ single: static asset urls
+
+.. _generating_static_asset_urls:
+
+Generating Static Asset URLs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a :meth:`pyramid.config.Configurator.add_static_view` method is used to
+register a static asset directory, a special helper API named
+:func:`pyramid.url.static_url` can be used to generate the appropriate URL
+for an asset that lives in one of the directories named by the static
+registration ``path`` attribute.
+
+For example, let's assume you create a set of static declarations like so:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_static_view(name='static1', path='mypackage:assets/1')
+ config.add_static_view(name='static2', path='mypackage:assets/2')
+
+These declarations create URL-accessible directories which have URLs that
+begin with ``/static1`` and ``/static2``, respectively. The assets in the
+``assets/1`` directory of the ``mypackage`` package are consulted when a user
+visits a URL which begins with ``/static1``, and the assets in the
+``assets/2`` directory of the ``mypackage`` package are consulted when a user
+visits a URL which begins with ``/static2``.
+
+You needn't generate the URLs to static assets "by hand" in such a
+configuration. Instead, use the :func:`pyramid.url.static_url` API to
+generate them for you. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.url import static_url
+ from pyramid.chameleon_zpt import render_template_to_response
+
+ def my_view(request):
+ css_url = static_url('mypackage:assets/1/foo.css', request)
+ js_url = static_url('mypackage:assets/2/foo.js', request)
+ return render_template_to_response('templates/my_template.pt',
+ css_url = css_url,
+ js_url = js_url)
+
+If the request "application URL" of the running system is
+``http://example.com``, the ``css_url`` generated above would be:
+``http://example.com/static1/foo.css``. The ``js_url`` generated
+above would be ``http://example.com/static2/foo.js``.
+
+One benefit of using the :func:`pyramid.url.static_url` function rather than
+constructing static URLs "by hand" is that if you need to change the ``name``
+of a static URL declaration, the generated URLs will continue to resolve
+properly after the rename.
+
+URLs may also be generated by :func:`pyramid.url.static_url` to static assets
+that live *outside* the :app:`Pyramid` application. This will happen when
+the :meth:`pyramid.config.Configurator.add_static_view` API associated with
+the path fed to :func:`pyramid.url.static_url` is a *URL* instead of a view
+name. For example, the ``name`` argument may be ``http://example.com`` while
+the the ``path`` given may be ``mypackage:images``:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_static_view(name='http://example.com/images',
+ path='mypackage:images')
+
+Under such a configuration, the URL generated by ``static_url`` for
+assets which begin with ``mypackage:images`` will be prefixed with
+``http://example.com/images``:
+
+.. code-block:: python
+ :linenos:
+
+ static_url('mypackage:images/logo.png', request)
+ # -> http://example.com/images/logo.png
+
+Using :func:`pyramid.url.static_url` in conjunction with a
+:meth:`pyramid.configuration.Configurator.add_static_view` makes it possible
+to put static media on a separate webserver during production (if the
+``name`` argument to :meth:`pyramid.config.Configurator.add_static_view` is a
+URL), while keeping static media package-internal and served by the
+development webserver during development (if the ``name`` argument to
+:meth:`pyramid.config.Configurator.add_static_view` is a URL prefix). To
+create such a circumstance, we suggest using the
+:attr:`pyramid.registry.Registry.settings` API in conjunction with a setting
+in the application ``.ini`` file named ``media_location``. Then set the
+value of ``media_location`` to either a prefix or a URL depending on whether
+the application is being run in development or in production (use a different
+`.ini`` file for production than you do for development). This is just a
+suggestion for a pattern; any setting name other than ``media_location``
+could be used.
+
+.. index::
+ single: static assets view
+
+.. _advanced_static:
+
+Advanced: Serving Static Assets Using a View Callable
+-----------------------------------------------------
+
+For more flexibility, static assets can be served by a :term:`view callable`
+which you register manually. For example, if you're using :term:`URL
+dispatch`, you may want static assets to only be available as a fallback if
+no previous route matches. Alternately, you might like to serve a particular
+static asset manually, because its download requires authentication.
+
+Note that you cannot use the :func:`pyramid.url.static_url` API to generate
+URLs against assets made accessible by registering a custom static view.
+
+Root-Relative Custom Static View (URL Dispatch Only)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :class:`pyramid.view.static` helper class generates a Pyramid view
+callable. This view callable can serve static assets from a directory. An
+instance of this class is actually used by the
+:meth:`pyramid.config.Configurator.add_static_view` configuration method, so
+its behavior is almost exactly the same once it's configured.
+
+.. warning:: The following example *will not work* for applications that use
+ :term:`traversal`, it will only work if you use :term:`URL dispatch`
+ exclusively. The root-relative route we'll be registering will always be
+ matched before traversal takes place, subverting any views registered via
+ ``add_view`` (at least those without a ``route_name``). A
+ :class:`pyramid.view.static` static view cannot be made root-relative when
+ you use traversal.
+
+To serve files within a directory located on your filesystem at
+``/path/to/static/dir`` as the result of a "catchall" route hanging from the
+root that exists at the end of your routing table, create an instance of the
+:class:`pyramid.view.static` class inside a ``static.py`` file in your
+application root as below.
+
+.. ignore-next-block
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import static
+ static_view = static('/path/to/static/dir')
+
+.. note:: For better cross-system flexibility, use an :term:`asset
+ specification` as the argument to :class:`pyramid.view.static` instead of
+ a physical absolute filesystem path, e.g. ``mypackage:static`` instead of
+ ``/path/to/mypackage/static``.
+
+Subsequently, you may wire the files that are served by this view up to be
+accessible as ``/<filename>`` using a configuration method in your
+application's startup code.
+
+.. code-block:: python
+ :linenos:
+
+ # .. every other add_route and/or add_handler declaration should come
+ # before this one, as it will, by default, catch all requests
+
+ config.add_route('catchall_static', '/*subpath', 'myapp.static.static_view')
+
+The special name ``*subpath`` above is used by the
+:class:`pyramid.view.static` view callable to signify the path of the file
+relative to the directory you're serving.
+
+Registering A View Callable to Serve a "Static" Asset
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can register a simple view callable to serve a single static asset. To
+do so, do things "by hand". First define the view callable.
+
+.. code-block:: python
+ :linenos:
+
+ import os
+ from webob import Response
+
+ def favicon_view(request):
+ here = os.path.dirname(__file__)
+ icon = open(os.path.join(here, 'static', 'favicon.ico'))
+ return Response(content_type='image/x-icon', app_iter=icon)
+
+The above bit of code within ``favicon_view`` computes "here", which is a
+path relative to the Python file in which the function is defined. It then
+uses the Python ``open`` function to obtain a file handle to a file within
+"here" named ``static``, and returns a response using the open the file
+handle as the response's ``app_iter``. It makes sure to set the right
+content_type too.
+
+You might register such a view via configuration as a view callable that
+should be called as the result of a traversal:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_view('myapp.views.favicon_view', name='favicon.ico')
+
+Or you might register it to be the view callable for a particular route:
+
+.. code-block:: python
+ :linenos:
+
+ config.add_route('favicon', '/favicon.ico',
+ view='myapp.views.favicon_view')
-:app:`Pyramid` uses the :term:`pkg_resources` API to resolve the package name
-and asset name to an absolute (operating-system-specific) file name. It
-eventually passes this resolved absolute filesystem path to the Chameleon
-templating engine, which then uses it to load, parse, and execute the
-template file.
+Because this is a simple view callable, it can be protected with a
+:term:`permission` or can be configured to respond under different
+circumstances using :term:`view predicate` arguments.
-Package names often contain dots. For example, ``pyramid`` is a package.
-Asset names usually look a lot like relative UNIX file paths.
.. index::
pair: overriding; assets
diff --git a/docs/narr/csrf.rst b/docs/narr/csrf.rst
new file mode 100644
index 000000000..7586b0ed7
--- /dev/null
+++ b/docs/narr/csrf.rst
@@ -0,0 +1,63 @@
+.. _csrf_chapter:
+
+Preventing Cross-Site Request Forgery Attacks
+=============================================
+
+`Cross-site request forgery
+<http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a
+phenomenon whereby a user with an identity on your website might click on a
+URL or button on another website which unwittingly redirects the user to your
+application to perform some command that requires elevated privileges.
+
+You can avoid most of these attacks by making sure that a the correct *CSRF
+token* has been set in an :app:`Pyramid` session object before performing any
+actions in code which requires elevated privileges and is invoked via a form
+post. To use CSRF token support, you must enable a :term:`session factory`
+as described in :ref:`using_the_default_session_factory` or
+:ref:`using_alternate_session_factories`.
+
+Using the ``session.new_csrf_token`` Method
+-------------------------------------------
+
+To add a CSRF token to the session, use the ``session.new_csrf_token`` method.
+
+.. code-block:: python
+ :linenos:
+
+ token = request.session.new_csrf_token()
+
+The ``.new_csrf_token`` method accepts no arguments. It returns a *token*
+string, which will be opaque and randomized. This token will also be set
+into the session, awaiting pickup by the ``session.get_csrf_token`` method.
+You can subsequently use the returned token as the value of a hidden field in
+a form that posts to a method that requires elevated privileges. The handler
+for the form post should use ``session.get_csrf_token`` (explained below) to
+obtain the current CSRF token related to the user from the session, and
+compare it to the value of the hidden form field.
+
+Using the ``session.get_csrf_token`` Method
+-------------------------------------------
+
+To get the current CSRF token from the session, use the
+``session.get_csrf_token`` method.
+
+.. code-block:: python
+ :linenos:
+
+ token = request.session.get_csrf_token()
+
+The ``get_csrf_token`` method accepts no arguments. It returns the "current"
+*token* string (as per the last call to ``session.new_csrf_token``). You can
+then use it to compare against the token provided within form post hidden
+value data. For example, if your form rendering included the CSRF token
+obtained via ``session.new_csrf_token`` as a hidden input field named
+``csrf_token``:
+
+.. code-block:: python
+ :linenos:
+
+ token = request.session.get_csrf_token()
+ if token != request.POST['csrf_token']:
+ raise ValueError('CSRF token did not match')
+
+
diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst
index deccb6c48..f36e55b29 100644
--- a/docs/narr/declarative.rst
+++ b/docs/narr/declarative.rst
@@ -3,28 +3,27 @@
Declarative Configuration
=========================
-The mode of configuration most comprehensively detailed by examples in
-narrative chapters in this book is "imperative" configuration. This is the
-configuration mode in which a developer cedes the least amount of control to
-the framework; it's "imperative" because you express the configuration
-directly in Python code, and you have the full power of Python at your
-disposal as you issue configuration statements. However, another mode of
-configuration exists within :app:`Pyramid`, which often provides better
-extensibility and configuration conflict detection.
+The mode of configuration detailed in the majority of examples within this
+this book is "imperative" configuration. This is the configuration mode in
+which a developer cedes the least amount of control to the framework; it's
+"imperative" because you express the configuration directly in Python code,
+and you have the full power of Python at your disposal as you issue
+configuration statements. However, another mode of configuration exists
+within :app:`Pyramid` named :term:`ZCML` which often provides better
+opportunity for extensibility.
A complete listing of ZCML directives is available within
:ref:`zcml_directives`. This chapter provides an overview of how you might
get started with ZCML and highlights some common tasks performed when you use
-ZCML. You can get a better understanding of when it's appropriate to use
-ZCML from :ref:`extending_chapter`.
+ZCML.
.. index::
single: declarative configuration
.. _declarative_configuration:
-Declarative Configuration
--------------------------
+ZCML Configuration
+------------------
A :app:`Pyramid` application can be configured "declaratively", if so
desired. Declarative configuration relies on *declarations* made external to
@@ -48,9 +47,7 @@ In a file named ``helloworld.py``:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -83,9 +80,7 @@ the ``if __name__ == '__main__'`` section of ``helloworld.py``:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.add_view(hello_world)
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -99,9 +94,7 @@ it now reads as:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -163,6 +156,8 @@ configure your application; instead you need to use :term:`ZCML`.
.. index::
single: ZCML conflict detection
+.. _zcml_conflict_detection:
+
ZCML Conflict Detection
~~~~~~~~~~~~~~~~~~~~~~~
@@ -224,9 +219,7 @@ To do so, first, create a file named ``helloworld.py``:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -269,10 +262,8 @@ within the ``if __name__ == '__main__'`` section of ``helloworld.py``:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.add_view(hello_world)
config.add_view(goodbye_world, name='goodbye')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -287,9 +278,7 @@ name='goodbye')``, so that it now reads as:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -345,6 +334,8 @@ contain other directives.
See also :ref:`configure_directive` and :ref:`word_on_xml_namespaces`.
+.. _the_include_tag:
+
The ``<include>`` Tag
~~~~~~~~~~~~~~~~~~~~~
@@ -478,6 +469,45 @@ declaratively. More information about this mode of configuration is
available in :ref:`declarative_configuration` and within
:ref:`zcml_reference`.
+.. index::
+ single: ZCML granularity
+
+ZCML Granularity
+~~~~~~~~~~~~~~~~
+
+It's extremely helpful to third party application "extenders" (aka
+"integrators") if the :term:`ZCML` that composes the configuration for an
+application is broken up into separate files which do very specific things.
+These more specific ZCML files can be reintegrated within the application's
+main ``configure.zcml`` via ``<include file="otherfile.zcml"/>``
+declarations. When ZCML files contain sets of specific declarations, an
+integrator can avoid including any ZCML he does not want by including only
+ZCML files which contain the declarations he needs. He is not forced to
+"accept everything" or "use nothing".
+
+For example, it's often useful to put all ``<route>`` declarations in a
+separate ZCML file, as ``<route>`` statements have a relative ordering that
+is extremely important to the application: if an extender wants to add a
+route to the "middle" of the routing table, he will always need to disuse all
+the routes and cut and paste the routing configuration into his own
+application. It's useful for the extender to be able to disuse just a
+*single* ZCML file in this case, accepting the remainder of the configuration
+from other :term:`ZCML` files in the original application.
+
+Granularizing ZCML is not strictly required. An extender can always disuse
+*all* your ZCML, choosing instead to copy and paste it into his own package,
+if necessary. However, doing so is considerate, and allows for the best
+reusability. Sometimes it's possible to include only certain ZCML files from
+an application that contain only the registrations you really need, omitting
+others. But sometimes it's not. For brute force purposes, when you're
+getting ``view`` or ``route`` registrations that you don't actually want in
+your overridden application, it's always appropriate to just *not include*
+any ZCML file from the overridden application. Instead, just cut and paste
+the entire contents of the ``configure.zcml`` (and any ZCML file included by
+the overridden application's ``configure.zcml``) into your own package and
+omit the ``<include package=""/>`` ZCML declaration in the overriding
+package's ``configure.zcml``.
+
.. _zcml_scanning:
Scanning via ZCML
@@ -503,9 +533,7 @@ file points to is scanned.
if __name__ == '__main__':
from pyramid.config import Configurator
config = Configurator()
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -1266,9 +1294,143 @@ which we assume lives in a ``subscribers.py`` module within your application:
See also :ref:`subscriber_directive` and :ref:`events_chapter`.
+.. index::
+ single: not found view
+
+.. _notfound_zcml:
+
+Configuring a Not Found View via ZCML
+-------------------------------------
+
+If your application uses :term:`ZCML`, you can replace the Not Found view by
+placing something like the following ZCML in your ``configure.zcml`` file.
+
+.. code-block:: xml
+ :linenos:
+
+ <view
+ view="helloworld.views.notfound_view"
+ context="pyramid.exceptions.NotFound"
+ />
+
+Replace ``helloworld.views.notfound_view`` with the Python dotted name to the
+notfound view you want to use.
+
+See :ref:`changing_the_notfound_view` for more information.
+
+.. index::
+ single: forbidden view
-.. Todo
-.. ----
+.. _forbidden_zcml:
+
+Configuring a Forbidden View via ZCML
+-------------------------------------
+
+If your application uses :term:`ZCML`, you can replace the Forbidden view by
+placing something like the following ZCML in your ``configure.zcml`` file.
+
+.. code-block:: xml
+ :linenos:
+
+ <view
+ view="helloworld.views.notfound_view"
+ context="pyramid.exceptions.Forbidden"
+ />
+
+Replace ``helloworld.views.forbidden_view`` with the Python dotted name to
+the forbidden view you want to use.
+
+See :ref:`changing_the_forbidden_view` for more information.
+
+.. _changing_traverser_zcml:
+
+Configuring an Alternate Traverser via ZCML
+-------------------------------------------
+
+Use an ``adapter`` stanza in your application's ``configure.zcml`` to
+change the default traverser:
+
+.. code-block:: xml
+ :linenos:
+
+ <adapter
+ factory="myapp.traversal.Traverser"
+ provides="pyramid.interfaces.ITraverser"
+ for="*"
+ />
+
+Or to register a traverser for a specific resource type:
+
+.. code-block:: xml
+ :linenos:
+
+ <adapter
+ factory="myapp.traversal.Traverser"
+ provides="pyramid.interfaces.ITraverser"
+ for="myapp.resources.MyRoot"
+ />
+
+See :ref:`changing_the_traverser` for more information.
+
+.. index::
+ single: url generator
+
+.. _changing_resource_url_zcml:
+
+Changing ``resource_url`` URL Generation via ZCML
+-------------------------------------------------
+
+You can change how :func:`pyramid.url.resource_url` generates a URL for a
+specific type of resource by adding an adapter statement to your
+``configure.zcml``.
+
+.. code-block:: xml
+ :linenos:
+
+ <adapter
+ factory="myapp.traversal.URLGenerator"
+ provides="pyramid.interfaces.IContextURL"
+ for="myapp.resources.MyRoot *"
+ />
+
+See :ref:`changing_resource_url` for more information.
+
+.. _changing_request_factory_zcml:
+
+Changing the Request Factory via ZCML
+-------------------------------------
+
+A ``MyRequest`` class can be registered via ZCML as a request factory through
+the use of the ZCML ``utility`` directive. In the below, we assume it lives
+in a package named ``mypackage.mymodule``.
+
+.. code-block:: xml
+ :linenos:
+
+ <utility
+ component="mypackage.mymodule.MyRequest"
+ provides="pyramid.interfaces.IRequestFactory"
+ />
+
+See :ref:`changing_request_factory` for more information.
+
+.. _adding_renderer_globals_zcml:
+
+Changing the Renderer Globals Factory via ZCML
+----------------------------------------------
+
+A renderer globals factory can be registered via ZCML as a through the use of
+the ZCML ``utility`` directive. In the below, we assume a
+``renderers_globals_factory`` function lives in a package named
+``mypackage.mymodule``.
+
+.. code-block:: xml
+ :linenos:
+
+ <utility
+ component="mypackage.mymodule.renderer_globals_factory"
+ provides="pyramid.interfaces.IRendererGlobalsFactory"
+ />
-.. - hooks chapter still has topics for ZCML
+See :ref:`adding_renderer_globals` for more information.
diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst
index 9802a01f6..524dcb2ac 100644
--- a/docs/narr/extending.rst
+++ b/docs/narr/extending.rst
@@ -3,11 +3,61 @@
Extending An Existing :app:`Pyramid` Application
===================================================
-If the developer of a :app:`Pyramid` application has obeyed certain
-constraints while building that application, a third party should be
-able to change its behavior without needing to modify its source code.
-The behavior of a :app:`Pyramid` application that obeys certain
-constraints can be *overridden* or *extended* without modification.
+If a :app:`Pyramid` developer has obeyed certain constraints while building
+an application, a third party should be able to change the application's
+behavior without needing to modify its source code. The behavior of a
+:app:`Pyramid` application that obeys certain constraints can be *overridden*
+or *extended* without modification.
+
+We'll define some jargon here for the benefit of identifying the parties
+involved in such an effort.
+
+Developer
+ The original application developer.
+
+Integrator
+ Another developer who wishes to reuse the application written by the
+ original application developer in an unanticipated context. He may also
+ wish to modify the original application without changing the original
+ application's source code.
+
+The Difference Between "Extensible" and "Pluggable" Applications
+----------------------------------------------------------------
+
+Other web frameworks, such as :term:`Django`, advertise that they allow
+developers to create "pluggable applications". They claim that if you create
+an application in a certain way, it will be integratable in a sensible,
+structured way into another arbitrarily-written application or project
+created by a third-party developer.
+
+:app:`Pyramid`, as a platform, does not claim to provide such a feature. The
+platform provides no guarantee that you can create an application and package
+it up such that an arbitrary integrator can use it as a subcomponent in a
+larger Pyramid application or project. Pyramid does not mandate the
+constraints necessary for such a pattern to work satisfactorily. Because
+Pyramid is not very "opinionated", developers are able to use wildly
+different patterns and technologies to build an application. A given Pyramid
+application may happen to be reusable by a particular third party integrator,
+because the integrator and the original developer may share similar base
+technology choices (such as the use of a particular relational database or
+ORM). But the same application may not be reusable by a different developer,
+because he has made different technology choices which are incompatible with
+the original developer's.
+
+As a result, the concept of a "pluggable application" is left to layers built
+above Pyramid, such as a "CMS" layer or "application server" layer. Such
+layers are apt to provide the necessary "opinions" (such as mandating a
+storage layer, a templating system, and a structured, well-documented pattern
+of registering that certain URLs map to certain bits of code) which makes the
+concept of a "pluggable application" possible. "Pluggable applications",
+thus, should not plug in to Pyramid itself but should instead plug into a
+system written atop Pyramid.
+
+Although it does not provide for "pluggable applications", Pyramid *does*
+provide a rich set of mechanisms which allows for the extension of a single
+existing application. Such features can be used by frameworks built using
+Pyramid as a base. All Pyramid applications may not be *pluggable*, but all
+Pyramid applications are *extensible*.
.. index::
single: extensible application
@@ -15,65 +65,65 @@ constraints can be *overridden* or *extended* without modification.
Rules for Building An Extensible Application
--------------------------------------------
-There's only one rule you need to obey if you want to build a
-maximally extensible :app:`Pyramid` application: you should not use
-any :term:`configuration decoration` or :term:`imperative
-configuration`. This means the application developer should avoid
-relying on :term:`configuration decoration` meant to be detected via
-a :term:`scan`, and you mustn't configure your :app:`Pyramid`
-application *imperatively* by using any code which configures the
-application through methods of the :term:`Configurator` (except for
-the :meth:`pyramid.config.Configurator.load_zcml` method).
-
-Instead, you must always use :term:`ZCML` for the equivalent
-purposes. :term:`ZCML` declarations that belong to an application can be
-"overridden" by integrators as necessary, but decorators and imperative code
-which perform the same tasks cannot. Use only :term:`ZCML` to configure your
-application if you'd like it to be extensible. See
+There is only one rule you need to obey if you want to build a maximally
+extensible :app:`Pyramid` application: as a developer, you should factor any
+overrideable :term:`imperative configuration` you've created into functions
+which can be used via :meth:`pyramid.config.Configurator.include` rather than
+inlined as calls to methods of a :term:`Configurator` within the ``main``
+function in your application's ``__init__.py``. For example, rather than:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.config import Configurator
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.add_view('myapp.views.view1', name='view1')
+ config.add_view('myapp.views.view2', name='view2')
+
+You should do move the calls to ``add_view`` outside of the (non-reusable)
+``if __name__ == '__main__'`` block, and into a reusable function:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.config import Configurator
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.include(add_views)
+
+ def add_views(config):
+ config.add_view('myapp.views.view1', name='view1')
+ config.add_view('myapp.views.view2', name='view2')
+
+Doing this allows an integrator to maximally reuse the configuration
+statements that relate to your application by allowing him to selectively
+include or disinclude the configuration functions you've created from an
+"override package".
+
+Alternately, you can use :term:`ZCML` for the purpose of making configuration
+extensible and overrideable. :term:`ZCML` declarations that belong to an
+application can be overridden and extended by integrators as necessary in a
+similar fashion. If you use only :term:`ZCML` to configure your application,
+it will automatically be maximally extensible without any manual effort. See
:ref:`declarative_chapter` for information about using ZCML.
Fundamental Plugpoints
~~~~~~~~~~~~~~~~~~~~~~
The fundamental "plug points" of an application developed using
-:app:`Pyramid` are *routes*, *views*, and *resources*. Routes are
-declarations made using the ZCML ``<route>`` directive. Views are
-declarations made using the ZCML ``<view>`` directive (or the
-``@view_config`` decorator). Resources are files that are accessed by
-:app:`Pyramid` using the :term:`pkg_resources` API such as static
-files and templates.
-
-.. index::
- single: ZCML granularity
-
-ZCML Granularity
-~~~~~~~~~~~~~~~~
-
-It's extremely helpful to third party application "extenders" (aka
-"integrators") if the :term:`ZCML` that composes the configuration for
-an application is broken up into separate files which do very specific
-things. These more specific ZCML files can be reintegrated within the
-application's main ``configure.zcml`` via ``<include
-file="otherfile.zcml"/>`` declarations. When ZCML files contain sets
-of specific declarations, an integrator can avoid including any ZCML
-he does not want by including only ZCML files which contain the
-declarations he needs. He is not forced to "accept everything" or
-"use nothing".
-
-For example, it's often useful to put all ``<route>`` declarations in
-a separate ZCML file, as ``<route>`` statements have a relative
-ordering that is extremely important to the application: if an
-extender wants to add a route to the "middle" of the routing table, he
-will always need to disuse all the routes and cut and paste the
-routing configuration into his own application. It's useful for the
-extender to be able to disuse just a *single* ZCML file in this case,
-accepting the remainder of the configuration from other :term:`ZCML`
-files in the original application.
-
-Granularizing ZCML is not strictly required. An extender can always
-disuse *all* your ZCML, choosing instead to copy and paste it into his
-own package, if necessary. However, doing so is considerate, and
-allows for the best reusability.
+:app:`Pyramid` are *routes*, *views*, and *assets*. Routes are declarations
+made using the :meth:`pyramid.config.Configurator.add_route` method (or the
+ZCML ``<route>`` directive). Views are declarations made using the
+:meth:`pyramid.config.Configurator.add_view` method (or the ZCML ``<view>``
+directive). Assets are files that are accessed by :app:`Pyramid` using the
+:term:`pkg_resources` API such as static files and templates via a
+:term:`asset specification`. Other directives and configurator methods also
+deal in routes, views, and assets. For example,
+:meth:`pyramid.config.Configurator.add_handler` adds a single route, and some
+number of views.
.. index::
single: extending an existing application
@@ -81,96 +131,88 @@ allows for the best reusability.
Extending an Existing Application
---------------------------------
-The steps for extending an existing application depend largely on
-whether the application does or does not use configuration decorators
-and/or imperative code.
+The steps for extending an existing application depend largely on whether the
+application does or does not use configuration decorators and/or imperative
+code.
+
+If The Application Has Configuration Decorations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You've inherited a :app:`Pyramid` application which you'd like to extend or
+override that uses :class:`pyramid.view.view_config` decorators or other
+:term:`configuration decoration` decorators.
-Extending an Application Which Possesses Configuration Decorators Or Which Does Configuration Imperatively
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If you just want to *extend* the application, you can run a :term:`scan`
+against the application's package, then add additional configuration that
+registers more views or routes.
-If you've inherited a :app:`Pyramid` application which uses
-:class:`pyramid.view.view_config` decorators or which performs
-configuration imperatively, one of two things may be true:
+.. code-block:: python
+ :linenos:
+
+ if __name__ == '__main__':
+ config.scan('someotherpackage')
+ config.add_view('mypackage.views.myview', name='myview')
-- If you just want to *extend* the application, you can write
- additional ZCML that registers more views or routes, loading any
- existing ZCML and continuing to use any existing imperative
- configuration done by the original application.
+If you want to *override* configuration in the application, you *may* need to
+run :meth:`pyramid.config.Configurator.commit` after performing the scan of
+the original package, then add additional configuration that registers more
+views or routes which performs overrides.
-- If you want to *override* configuration in the application, you
- *may* need to change the source code of the original application.
+.. code-block:: python
+ :linenos:
- If the only source of trouble is the existence of
- :class:`pyramid.view.view_config` decorators, you can just prevent a
- :term:`scan` from happening (by omitting the ``<scan>`` declaration
- from ZCML or omitting any call to the
- :meth:`pyramid.config.Configurator.scan` method). This
- will cause the decorators to do nothing. At this point, you will
- need to convert all the configuration done in decorators into
- equivalent :term:`ZCML` and add that ZCML to a separate Python
- package as described in :ref:`extending_the_application`.
+ if __name__ == '__main__':
+ config.scan('someotherpackage')
+ config.commit()
+ config.add_view('mypackage.views.myview', name='myview'
- If the source of trouble is configuration done imperatively in a
- function called during application startup, you'll need to change
- the code: convert imperative configuration statements into
- equivalent :term:`ZCML` declarations.
+Once this is done, you should be able to extend or override the application
+like any other (see :ref:`extending_the_application`).
-Once this is done, you should be able to extend or override the
-application like any other (see :ref:`extending_the_application`).
+You can alternately just prevent a :term:`scan` from happening (by omitting
+any call to the :meth:`pyramid.config.Configurator.scan` method). This will
+cause the decorators attached to objects in the target application to do
+nothing. At this point, you will need to convert all the configuration done
+in decorators into equivalent imperative configuration or ZCML and add that
+configuration or ZCML to a separate Python package as described in
+:ref:`extending_the_application`.
.. _extending_the_application:
-Extending an Application Which Does Not Possess Configuration Decorators or Imperative Configuration
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To extend or override the behavior of an existing application, you
-will need to write some :term:`ZCML`, and perhaps some implementations
-of the types of things you'd like to override (such as views), which
-are referred to within that ZCML.
-
-The general pattern for extending an existing application looks
-something like this:
-
-- Create a new Python package. The easiest way to do this is to
- create a new :app:`Pyramid` application using the "paster"
- template mechanism. See :ref:`creating_a_project` for more
- information.
-
-- Install the new package into the same Python environment as the
- original application (e.g. ``python setup.py develop`` or ``python
- setup.py install``).
-
-- Change the ``configure.zcml`` in the new package to include the
- original :app:`Pyramid` application's ``configure.zcml`` via an
- include statement, e.g. ``<include package="theoriginalapp"/>``.
- Alternately, if the original application writer anticipated
- overriding some things and not others, instead of including the
- "main" ``configure.zcml`` of the original application, include only
- specific ZCML files from the original application using the ``file``
- attribute of the ``<include>`` statement, e.g. ``<include
- package="theoriginalapp" file="views.zcml"/>``.
-
-- On a line in the new package's ``configure.zcml`` file that falls
- after (XML-ordering-wise) all the ``include`` statements of the original
- package ZCML, put an ``includeOverrides`` statement which identifies
- *another* ZCML file within the new package (for example
- ``<includeOverrides file="overrides.zcml"/>``.
-
-- Create an ``overrides.zcml`` file within the new package. The
- statements in the ``overrides.zcml`` file will override any ZCML
- statements made within the original application (such as view
- declarations).
-
-- Create Python files containing views and other overridden elements,
- such as templates and static resources as necessary, and wire these
- up using ZCML registrations within the ``overrides.zcml`` file.
- These registrations may extend or override the original view
- registrations. See :ref:`overriding_views`,
- :ref:`overriding_routes` and :ref:`overriding_resources`.
+Extending the Application
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To extend or override the behavior of an existing application, you will need
+to create a new package which includes the configuration of the old package,
+and you'll perhaps need to create implementations of the types of things
+you'd like to override (such as views), which are referred to within the
+original package.
-- In the ``__init__.py`` of the new package, load the ``configure.zcml`` file
- of the new package using the
- :meth:`pyramid.config.Configurator.load_zcml` method.
+The general pattern for extending an existing application looks something
+like this:
+
+- Create a new Python package. The easiest way to do this is to create a new
+ :app:`Pyramid` application using the "paster" template mechanism. See
+ :ref:`creating_a_project` for more information.
+
+- In the new package, create Python files containing views and other
+ overridden elements, such as templates and static resources as necessary.
+
+- Install the new package into the same Python environment as the original
+ application (e.g. ``python setup.py develop`` or ``python setup.py
+ install``).
+
+- Change the ``main`` function in the new package's ``__init__py`` to include
+ the original :app:`Pyramid` application's configuration functions via
+ :meth:`pyramid.config.Configurator.include` statements or a :term:`scan`.
+
+- Wire the new views and assets created in the new package up using
+ imperative registrations within the ``main`` function of the
+ ``__init__.py`` file of the new application. These wiring should happen
+ *after* including the configuration functions of the old application.
+ These registrations will extend or override any registrations performed by
+ the original application. See :ref:`overriding_views`,
+ :ref:`overriding_routes` and :ref:`overriding_resources`.
.. index::
pair: overriding; views
@@ -180,26 +222,44 @@ something like this:
Overriding Views
~~~~~~~~~~~~~~~~~
-The ZCML ``<view>`` declarations you make which *override* application
-behavior will usually have the same ``context`` and ``name`` (and
-:term:`predicate` attributes, if used) as the original. These
-``<view>`` declarations will point at "new" view code. The new view
-code itself will usually be cut-n-paste copies of view callables from
-the original application with slight tweaks. For example:
+The :term:`view configuration` declarations you make which *override*
+application behavior will usually have the same :term:`view predicate`
+attributes as the original you wish to override. These ``<view>``
+declarations will point at "new" view code, in the override package you've
+created. The new view code itself will usually be cut-n-paste copies of view
+callables from the original application with slight tweaks.
+
+For example, if the original application has the following
+``configure_views`` configuration method:
+
+.. code-block:: python
+ :linenos:
+
+ def configure_views(config):
+ config.add_view('theoriginalapp.views.theview', name='theview')
-.. code-block:: xml
+You can override the first view configuration statement made by
+``configure_views`` within the override package, after loading the original
+configuration function:
+
+.. code-block:: python
:linenos:
- <view
- context="theoriginalapplication.resources.SomeResource"
- name="theview"
- view=".views.a_view_that_does_something_slightly_different"
- />
+ from pyramid.config import Configurator
+ from originalapp import configure_views
+
+ if __name == '__main__':
+ config = Configurator()
+ config.include(configure_views)
+ config.add_view('theoverrideapp.views.theview', name='theview')
+
+In this case, the ``theoriginalapp.views.theview`` view will never be
+executed. Instead, a new view, ``theoverrideapp.views.theview`` will be
+executed instead, when request circumstances dictate.
-A similar pattern can be used to *extend* the application with ``<view>``
-declarations. Just register a new view against some existing resource type
-(using ``context``) and make sure the URLs it implies are available on some
-other page rendering.
+A similar pattern can be used to *extend* the application with ``add_view``
+declarations. Just register a new view against some other set of predicates
+to make sure the URLs it implies are available on some other page rendering.
.. index::
pair: overriding; routes
@@ -209,48 +269,27 @@ other page rendering.
Overriding Routes
~~~~~~~~~~~~~~~~~
-Route setup is currently typically performed in a sequence of ordered
-ZCML ``<route>`` declarations. Because these declarations are ordered
-relative to each other, and because this ordering is typically
-important, you should retain the relative ordering of these
-declarations when performing an override. Typically, this means
-*copying* all the ``<route>`` declarations into an external ZCML file
-and changing them as necessary. Then disinclude any ZCML from the
-original application which contains the original declarations.
+Route setup is currently typically performed in a sequence of ordered calls
+to :meth:`pyramid.config.Configurator.add_route`. Because these calls are
+ordered relative to each other, and because this ordering is typically
+important, you should retain their relative ordering when performing an
+override. Typically, this means *copying* all the ``add_route`` statements
+into the override package's file and changing them as necessary. Then
+disinclude any ``add_route`` statements from the original application.
.. index::
pair: overriding; resources
.. _overriding_resources:
-Overriding Resources
-~~~~~~~~~~~~~~~~~~~~
-
-"Resource" files are static files on the filesystem that are
-accessible within a Python *package*. An entire chapter is devoted to
-resources: :ref:`resources_chapter`. Within this chapter is a section
-named :ref:`overriding_resources_section`. This section of that
-chapter describes in detail how to override package resources with
-other resources by using :term:`ZCML` ``<resource>`` declarations. Add
-such ``<resource>`` declarations to your override package's
-``configure.zcml`` to perform overrides.
-
-.. index::
- single: ZCML inclusion
-
-Dealing With ZCML Inclusions
-----------------------------
-
-Sometimes it's possible to include only certain ZCML files from an
-application that contain only the registrations you really need,
-omitting others. But sometimes it's not. For brute force purposes,
-when you're getting ``view`` or ``route`` registrations that you don't
-actually want in your overridden application, it's always appropriate
-to just *not include* any ZCML file from the overridden application.
-Instead, just cut and paste the entire contents of the
-``configure.zcml`` (and any ZCML file included by the overridden
-application's ``configure.zcml``) into your own package and omit the
-``<include package=""/>`` ZCML declaration in the overriding package's
-``configure.zcml``.
-
+Overriding Assets
+~~~~~~~~~~~~~~~~~
+Assets are files on the filesystem that are accessible within a Python
+*package*. An entire chapter is devoted to resources: :ref:`assets_chapter`.
+Within this chapter is a section named :ref:`overriding_assets_section`.
+This section of that chapter describes in detail how to override package
+resources with other resources by using the
+:meth:`pyramid.config.Configurator.override_asset` method. Add such
+``override_asset`` calls to your override package's ``__init__.py`` to
+perform overrides.
diff --git a/docs/narr/flash.rst b/docs/narr/flash.rst
new file mode 100644
index 000000000..d41c2cdaf
--- /dev/null
+++ b/docs/narr/flash.rst
@@ -0,0 +1,107 @@
+.. _flash_chapter:
+
+Flash Messages
+==============
+
+"Flash messages" are simply a queue of message strings stored in the
+:term:`session`. To use flash messaging, you must enable a :term:`session
+factory` as described in :ref:`using_the_default_session_factory` or
+:ref:`using_alternate_session_factories`.
+
+Flash messaging has two main uses: to display a status message only once to
+the user after performing an internal redirect, and to allow generic code to
+log messages for single-time display without having direct access to an HTML
+template. The user interface consists of a number of methods of the
+:term:`session` object.
+
+Using the ``session.flash`` Method
+----------------------------------
+
+To add a message to a flash message queue, use a session object's ``flash``
+method:
+
+.. code-block:: python
+ :linenos:
+
+ request.session.flash('mymessage')
+
+The ``.flash`` method appends a message to a flash queue, creating the queue
+if necessary.
+
+``.flash`` accepts three arguments:
+
+.. method:: flash(message, queue='', allow_duplicate=True)
+
+The ``message`` argument is required. It represents a message you wish to
+later display to a user. It is usually a string but the ``message`` you
+provide is not modified in any way.
+
+The ``queue`` argument allows you to choose a queue to which to append the
+message you provide. This can be used to push different kinds of messages
+into flash storage for later display in different places on a page. You cam
+pass any name for your queue, but it must be a string. The default value is
+the empty string, which chooses the default queue. Each queue is independent,
+and can be popped by ``pop_flash`` or examined via ``peek_flash`` separately.
+``queue`` defaults to the empty string. The empty string represents the
+default flash message queue.
+
+.. code-block:: python
+
+ request.session.flash(msg, 'myappsqueue')
+
+The ``allow_duplicate`` argument, which defaults to ``True``. If this is
+``False``, if you attempt to add a message to a queue which is already
+present in the queue, it will not be added.
+
+Using the ``session.pop_flash`` Method
+--------------------------------------
+
+Once one or more messages has been added to a flash queue by the
+``session.flash`` API, the ``session.pop_flash`` API can be used to pop that
+queue and return it for use.
+
+To pop a particular queue of messages from the flash object, use the session
+object's ``pop_flash`` method.
+
+.. code-block:: python
+ :linenos:
+
+ >>> request.session.flash('info message')
+ >>> request.session.pop_flash()
+ ['info message']
+
+Calling ``session.pop_flash()`` again like above without a corresponding call
+to ``session.flash`` will return an empty list, because the queue has already
+been popped.
+
+.. code-block:: python
+ :linenos:
+
+ >>> request.session.flash('info message')
+ >>> request.session.pop_flash()
+ ['info message']
+ >>> request.session.pop_flash()
+ []
+
+The object returned from ``pop_flash`` is a list.
+
+Using the ``session.pop_flash`` Method
+--------------------------------------
+
+Once one or more messages has been added to a flash queue by the
+``session.flash`` API, the ``session.peek_flash`` API can be used to "peek"
+at that queue. Unlike ``session.pop_flash``, the queue is not popped from
+flash storage.
+
+.. code-block:: python
+ :linenos:
+
+ >>> request.session.flash('info message')
+ >>> request.session.peek_flash()
+ ['info message']
+ >>> request.session.peek_flash()
+ ['info message']
+ >>> request.session.pop_flash()
+ ['info message']
+ >>> request.session.peek_flash()
+ []
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 006f5d5cb..2917b5254 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -3,8 +3,8 @@
Using Hooks
===========
-"Hooks" can be used to influence the behavior of the :app:`Pyramid`
-framework in various ways.
+"Hooks" can be used to influence the behavior of the :app:`Pyramid` framework
+in various ways.
.. index::
single: not found view
@@ -14,61 +14,38 @@ framework in various ways.
Changing the Not Found View
---------------------------
-When :app:`Pyramid` can't map a URL to view code, it invokes a
-:term:`not found view`, which is a :term:`view callable`. A default
-notfound view exists. The default not found view can be overridden
-through application configuration. This override can be done via
-:term:`imperative configuration` or :term:`ZCML`.
+When :app:`Pyramid` can't map a URL to view code, it invokes a :term:`not
+found view`, which is a :term:`view callable`. A default notfound view
+exists. The default not found view can be overridden through application
+configuration.
-The :term:`not found view` callable is a view callable like any other.
-The :term:`view configuration` which causes it to be a "not found"
-view consists only of naming the :exc:`pyramid.exceptions.NotFound`
-class as the ``context`` of the view configuration.
+The :term:`not found view` callable is a view callable like any other. The
+:term:`view configuration` which causes it to be a "not found" view consists
+only of naming the :exc:`pyramid.exceptions.NotFound` class as the
+``context`` of the view configuration.
-.. topic:: Using Imperative Configuration
+If your application uses :term:`imperative configuration`, you can replace
+the Not Found view by using the :meth:`pyramid.config.Configurator.add_view`
+method to register an "exception view":
- If your application uses :term:`imperative configuration`, you can
- replace the Not Found view by using the
- :meth:`pyramid.config.Configurator.add_view` method to
- register an "exception view":
-
- .. code-block:: python
- :linenos:
-
- from pyramid.exceptions import NotFound
- from helloworld.views import notfound_view
- config.add_view(notfound_view, context=NotFound)
-
- Replace ``helloworld.views.notfound_view`` with a reference to the
- Python :term:`view callable` you want to use to represent the Not
- Found view.
-
-.. topic:: Using ZCML
-
- If your application uses :term:`ZCML`, you can replace the Not Found
- view by placing something like the following ZCML in your
- ``configure.zcml`` file.
-
- .. code-block:: xml
- :linenos:
+.. code-block:: python
+ :linenos:
- <view
- view="helloworld.views.notfound_view"
- context="pyramid.exceptions.NotFound"
- />
+ from pyramid.exceptions import NotFound
+ from helloworld.views import notfound_view
+ config.add_view(notfound_view, context=NotFound)
- Replace ``helloworld.views.notfound_view`` with the Python dotted name
- to the notfound view you want to use.
+Replace ``helloworld.views.notfound_view`` with a reference to the
+:term:`view callable` you want to use to represent the Not Found view.
-Like any other view, the notfound view must accept at least a
-``request`` parameter, or both ``context`` and ``request``. The
-``request`` is the current :term:`request` representing the denied
-action. The ``context`` (if used in the call signature) will be the
-instance of the :exc:`pyramid.exceptions.NotFound` exception that
-caused the view to be called.
+Like any other view, the notfound view must accept at least a ``request``
+parameter, or both ``context`` and ``request``. The ``request`` is the
+current :term:`request` representing the denied action. The ``context`` (if
+used in the call signature) will be the instance of the
+:exc:`pyramid.exceptions.NotFound` exception that caused the view to be
+called.
-Here's some sample code that implements a minimal NotFound view
-callable:
+Here's some sample code that implements a minimal NotFound view callable:
.. code-block:: python
:linenos:
@@ -93,6 +70,9 @@ callable:
:exc:`pyramid.exceptions.NotFound` exception instance. If available, the
resource context will still be available as ``request.context``.
+For information about how to configure a not found view via :term:`ZCML`, see
+:ref:`notfound_zcml`.
+
.. index::
single: forbidden view
@@ -101,59 +81,36 @@ callable:
Changing the Forbidden View
---------------------------
-When :app:`Pyramid` can't authorize execution of a view based on
-the :term:`authorization policy` in use, it invokes a :term:`forbidden
-view`. The default forbidden response has a 401 status code and is
-very plain, but the view which generates it can be overridden as
-necessary using either :term:`imperative configuration` or
-:term:`ZCML`.
-
-The :term:`forbidden view` callable is a view callable like any other.
-The :term:`view configuration` which causes it to be a "not found"
-view consists only of naming the :exc:`pyramid.exceptions.Forbidden`
-class as the ``context`` of the view configuration.
-
-.. topic:: Using Imperative Configuration
-
- If your application uses :term:`imperative configuration`, you can
- replace the Forbidden view by using the
- :meth:`pyramid.config.Configurator.add_view` method to
- register an "exception view":
-
- .. code-block:: python
- :linenos:
-
- from helloworld.views import forbidden_view
- from pyramid.exceptions import Forbidden
- config.add_view(forbidden_view, context=Forbidden)
-
- Replace ``helloworld.views.forbidden_view`` with a reference to the
- Python :term:`view callable` you want to use to represent the
- Forbidden view.
+When :app:`Pyramid` can't authorize execution of a view based on the
+:term:`authorization policy` in use, it invokes a :term:`forbidden view`.
+The default forbidden response has a 401 status code and is very plain, but
+the view which generates it can be overridden as necessary.
-.. topic:: Using ZCML
+The :term:`forbidden view` callable is a view callable like any other. The
+:term:`view configuration` which causes it to be a "not found" view consists
+only of naming the :exc:`pyramid.exceptions.Forbidden` class as the
+``context`` of the view configuration.
- If your application uses :term:`ZCML`, you can replace the
- Forbidden view by placing something like the following ZCML in your
- ``configure.zcml`` file.
+You can replace the forbidden view by using the
+:meth:`pyramid.config.Configurator.add_view` method to register an "exception
+view":
- .. code-block:: xml
- :linenos:
+.. code-block:: python
+ :linenos:
- <view
- view="helloworld.views.notfound_view"
- context="pyramid.exceptions.Forbidden"
- />
+ from helloworld.views import forbidden_view
+ from pyramid.exceptions import Forbidden
+ config.add_view(forbidden_view, context=Forbidden)
- Replace ``helloworld.views.forbidden_view`` with the Python
- dotted name to the forbidden view you want to use.
+Replace ``helloworld.views.forbidden_view`` with a reference to the Python
+:term:`view callable` you want to use to represent the Forbidden view.
-Like any other view, the forbidden view must accept at least a
-``request`` parameter, or both ``context`` and ``request``. The
-``context`` (available as ``request.context`` if you're using the
-request-only view argument pattern) is the context found by the router
-when the view invocation was denied. The ``request`` is the current
-:term:`request` representing the denied action.
+Like any other view, the forbidden view must accept at least a ``request``
+parameter, or both ``context`` and ``request``. The ``context`` (available
+as ``request.context`` if you're using the request-only view argument
+pattern) is the context found by the router when the view invocation was
+denied. The ``request`` is the current :term:`request` representing the
+denied action.
Here's some sample code that implements a minimal forbidden view:
@@ -161,10 +118,10 @@ Here's some sample code that implements a minimal forbidden view:
:linenos:
from pyramid.views import view_config
+ from pyramid.response import Response
- @view_config(renderer='templates/login_form.pt')
def forbidden_view(request):
- return {}
+ return Response('forbidden')
.. note:: When a forbidden view callable is invoked, it is passed a
:term:`request`. The ``exception`` attribute of the request will
@@ -181,164 +138,26 @@ Here's some sample code that implements a minimal forbidden view:
an alternate forbidden view. For example, it would make sense to
return a response with a ``403 Forbidden`` status code.
-.. index::
- single: traverser
-
-.. _changing_the_traverser:
-
-Changing the Traverser
-----------------------
-
-The default :term:`traversal` algorithm that :app:`Pyramid` uses is
-explained in :ref:`traversal_algorithm`. Though it is rarely
-necessary, this default algorithm can be swapped out selectively for a
-different traversal pattern via configuration.
-
-Use an ``adapter`` stanza in your application's ``configure.zcml`` to
-change the default traverser:
-
-.. code-block:: xml
- :linenos:
-
- <adapter
- factory="myapp.traversal.Traverser"
- provides="pyramid.interfaces.ITraverser"
- for="*"
- />
-
-In the example above, ``myapp.traversal.Traverser`` is assumed to be
-a class that implements the following interface:
-
-.. code-block:: python
- :linenos:
-
- class Traverser(object):
- def __init__(self, root):
- """ Accept the root object returned from the root factory """
-
- def __call__(self, request):
- """ Return a dictionary with (at least) the keys ``root``,
- ``context``, ``view_name``, ``subpath``, ``traversed``,
- ``virtual_root``, and ``virtual_root_path``. These values are
- typically the result of a resource tree traversal. ``root``
- is the physical root object, ``context`` will be a resource
- object, ``view_name`` will be the view name used (a Unicode
- name), ``subpath`` will be a sequence of Unicode names that
- followed the view name but were not traversed, ``traversed``
- will be a sequence of Unicode names that were traversed
- (including the virtual root path, if any) ``virtual_root``
- will be a resource object representing the virtual root (or the
- physical root if traversal was not performed), and
- ``virtual_root_path`` will be a sequence representing the
- virtual root path (a sequence of Unicode names) or None if
- traversal was not performed.
-
- Extra keys for special purpose functionality can be added as
- necessary.
-
- All values returned in the dictionary will be made available
- as attributes of the ``request`` object.
- """
-
-More than one traversal algorithm can be active at the same time. For
-instance, if your :term:`root factory` returns more than one type of
-object conditionally, you could claim that an alternate traverser
-adapter is ``for`` only one particular class or interface. When the
-root factory returned an object that implemented that class or
-interface, a custom traverser would be used. Otherwise, the default
-traverser would be used. For example:
-
-.. code-block:: xml
- :linenos:
-
- <adapter
- factory="myapp.traversal.Traverser"
- provides="pyramid.interfaces.ITraverser"
- for="myapp.resources.MyRoot"
- />
-
-If the above stanza was added to a ``configure.zcml`` file,
-:app:`Pyramid` would use the ``myapp.traversal.Traverser`` only
-when the application :term:`root factory` returned an instance of the
-``myapp.resources.MyRoot`` object. Otherwise it would use the default
-:app:`Pyramid` traverser to do traversal.
+For information about how to configure a forbidden view via :term:`ZCML`, see
+:ref:`forbidden_zcml`.
.. index::
- single: url generator
-
-Changing How :mod:`pyramid.url.resource_url` Generates a URL
-------------------------------------------------------------
-
-When you add a traverser as described in :ref:`changing_the_traverser`, it's
-often convenient to continue to use the :func:`pyramid.url.resource_url` API.
-However, since the way traversal is done will have been modified, the URLs it
-generates by default may be incorrect.
-
-If you've added a traverser, you can change how
-:func:`pyramid.url.resource_url` generates a URL for a specific type of
-resource by adding an adapter stanza for
-:class:`pyramid.interfaces.IContextURL` to your application's
-``configure.zcml``:
-
-.. code-block:: xml
- :linenos:
-
- <adapter
- factory="myapp.traversal.URLGenerator"
- provides="pyramid.interfaces.IContextURL"
- for="myapp.resources.MyRoot *"
- />
-
-In the above example, the ``myapp.traversal.URLGenerator`` class will
-be used to provide services to :func:`pyramid.url.resource_url` any
-time the :term:`context` passed to ``resource_url`` is of class
-``myapp.resources.MyRoot``. The asterisk following represents the type
-of interface that must be possessed by the :term:`request` (in this
-case, any interface, represented by asterisk).
-
-The API that must be implemented by a class that provides
-:class:`pyramid.interfaces.IContextURL` is as follows:
-
-.. code-block:: python
- :linenos:
-
- from zope.interface import Interface
-
- class IContextURL(Interface):
- """ An adapter which deals with URLs related to a context.
- """
- def __init__(self, context, request):
- """ Accept the context and request """
-
- def virtual_root(self):
- """ Return the virtual root object related to a request and the
- current context"""
-
- def __call__(self):
- """ Return a URL that points to the context """
-
-The default context URL generator is available for perusal as the
-class :class:`pyramid.traversal.TraversalContextURL` in the
-`traversal module
-<http://github.com/Pylons/pyramid/blob/master/pyramid/traversal.py>`_ of
-the :term:`Pylons` GitHub Pyramid repository.
+ single: request factory
.. _changing_the_request_factory:
Changing the Request Factory
----------------------------
-Whenever :app:`Pyramid` handles a :term:`WSGI` request, it creates
-a :term:`request` object based on the WSGI environment it has been
-passed. By default, an instance of the
-:class:`pyramid.request.Request` class is created to represent the
-request object.
+Whenever :app:`Pyramid` handles a :term:`WSGI` request, it creates a
+:term:`request` object based on the WSGI environment it has been passed. By
+default, an instance of the :class:`pyramid.request.Request` class is created
+to represent the request object.
-The class (aka "factory") that :app:`Pyramid` uses to create a
-request object instance can be changed by passing a
-``request_factory`` argument to the constructor of the
-:term:`configurator`. This argument can be either a callable or a
-:term:`dotted Python name` representing a callable.
+The class (aka "factory") that :app:`Pyramid` uses to create a request object
+instance can be changed by passing a ``request_factory`` argument to the
+constructor of the :term:`configurator`. This argument can be either a
+callable or a :term:`dotted Python name` representing a callable.
.. code-block:: python
:linenos:
@@ -350,24 +169,9 @@ request object instance can be changed by passing a
config = Configurator(request_factory=MyRequest)
-The same ``MyRequest`` class can alternately be registered via ZCML as
-a request factory through the use of the ZCML ``utility`` directive.
-In the below, we assume it lives in a package named
-``mypackage.mymodule``.
-
-.. code-block:: xml
- :linenos:
-
- <utility
- component="mypackage.mymodule.MyRequest"
- provides="pyramid.interfaces.IRequestFactory"
- />
-
-Lastly, if you're doing imperative configuration, and you'd rather do
-it after you've already constructed a :term:`configurator` it can also
-be registered via the
-:meth:`pyramid.config.Configurator.set_request_factory`
-method:
+If you're doing imperative configuration, and you'd rather do it after you've
+already constructed a :term:`configurator` it can also be registered via the
+:meth:`pyramid.config.Configurator.set_request_factory` method:
.. code-block:: python
:linenos:
@@ -381,26 +185,29 @@ method:
config = Configurator()
config.set_request_factory(MyRequest)
+To use ZCML for the same purpose, see :ref:`changing_request_factory_zcml`.
+
+.. index::
+ single: renderer globals
+
.. _adding_renderer_globals:
Adding Renderer Globals
-----------------------
-Whenever :app:`Pyramid` handles a request to perform a rendering
-(after a view with a ``renderer=`` configuration attribute is invoked,
-or when the any of the methods beginning with ``render`` within the
-:mod:`pyramid.renderers` module are called), *renderer globals* can
-be injected into the *system* values sent to the renderer. By
-default, no renderer globals are injected, and the "bare" system
-values (such as ``request``, ``context``, and ``renderer_name``) are
-the only values present in the system dictionary passed to every
-renderer.
-
-A callback that :app:`Pyramid` will call every time a renderer is
-invoked can be added by passing a ``renderer_globals_factory``
-argument to the constructor of the :term:`configurator`. This
-callback can either be a callable object or a :term:`dotted Python
-name` representing such a callable.
+Whenever :app:`Pyramid` handles a request to perform a rendering (after a
+view with a ``renderer=`` configuration attribute is invoked, or when the any
+of the methods beginning with ``render`` within the :mod:`pyramid.renderers`
+module are called), *renderer globals* can be injected into the *system*
+values sent to the renderer. By default, no renderer globals are injected,
+and the "bare" system values (such as ``request``, ``context``, and
+``renderer_name``) are the only values present in the system dictionary
+passed to every renderer.
+
+A callback that :app:`Pyramid` will call every time a renderer is invoked can
+be added by passing a ``renderer_globals_factory`` argument to the
+constructor of the :term:`configurator`. This callback can either be a
+callable object or a :term:`dotted Python name` representing such a callable.
.. code-block:: python
:linenos:
@@ -411,30 +218,15 @@ name` representing such a callable.
config = Configurator(
renderer_globals_factory=renderer_globals_factory)
-Such a callback must accept a single positional argument (notionally
-named ``system``) which will contain the original system values. It
-must return a dictionary of values that will be merged into the system
-dictionary. See :ref:`renderer_system_values` for discription of the
-values present in the system dictionary.
+Such a callback must accept a single positional argument (notionally named
+``system``) which will contain the original system values. It must return a
+dictionary of values that will be merged into the system dictionary. See
+:ref:`renderer_system_values` for discription of the values present in the
+system dictionary.
-A renderer globals factory can alternately be registered via ZCML as a
-through the use of the ZCML ``utility`` directive. In the below, we
-assume a ``renderers_globals_factory`` function lives in a package
-named ``mypackage.mymodule``.
-
-.. code-block:: xml
- :linenos:
-
- <utility
- component="mypackage.mymodule.renderer_globals_factory"
- provides="pyramid.interfaces.IRendererGlobalsFactory"
- />
-
-Lastly, if you're doing imperative configuration, and you'd rather do
-it after you've already constructed a :term:`configurator` it can also
-be registered via the
-:meth:`pyramid.config.Configurator.set_renderer_globals_factory`
-method:
+If you're doing imperative configuration, and you'd rather do it after you've
+already constructed a :term:`configurator` it can also be registered via the
+:meth:`pyramid.config.Configurator.set_renderer_globals_factory` method:
.. code-block:: python
:linenos:
@@ -450,6 +242,12 @@ method:
Another mechanism which allows event subscribers to add renderer global values
exists in :ref:`beforerender_event`.
+If you'd rather ZCML to register a renderer globals factory, see
+:ref:`adding_renderer_globals_zcml`.
+
+.. index::
+ single: before render event
+
.. _beforerender_event:
Using The Before Render Event
@@ -472,8 +270,8 @@ that can be used for this purpose. For example:
An object of this type is sent as an event just before a :term:`renderer` is
invoked (but *after* the application-level renderer globals factory added via
-:class:`pyramid.config.Configurator.set_renderer_globals_factory`, if
-any, has injected its own keys into the renderer globals dictionary).
+:class:`pyramid.config.Configurator.set_renderer_globals_factory`, if any,
+has injected its own keys into the renderer globals dictionary).
If a subscriber attempts to add a key that already exist in the renderer
globals dictionary, a :exc:`KeyError` is raised. This limitation is enforced
@@ -488,21 +286,24 @@ interface at :class:`pyramid.interfaces.IBeforeRender`.
Another mechanism which allows event subscribers more control when adding
renderer global values exists in :ref:`adding_renderer_globals`.
+.. index::
+ single: response callback
+
.. _using_response_callbacks:
Using Response Callbacks
------------------------
-Unlike many other web frameworks, :app:`Pyramid` does not eagerly
-create a global response object. Adding a :term:`response callback`
-allows an application to register an action to be performed against a
-response object once it is created, usually in order to mutate it.
+Unlike many other web frameworks, :app:`Pyramid` does not eagerly create a
+global response object. Adding a :term:`response callback` allows an
+application to register an action to be performed against a response object
+once it is created, usually in order to mutate it.
-The :meth:`pyramid.request.Request.add_response_callback` method is
-used to register a response callback.
+The :meth:`pyramid.request.Request.add_response_callback` method is used to
+register a response callback.
-A response callback is a callable which accepts two positional
-parameters: ``request`` and ``response``. For example:
+A response callback is a callable which accepts two positional parameters:
+``request`` and ``response``. For example:
.. code-block:: python
:linenos:
@@ -515,11 +316,11 @@ parameters: ``request`` and ``response``. For example:
No response callback is called if an unhandled exception happens in
application code, or if the response object returned by a :term:`view
-callable` is invalid. Response callbacks *are*, however, invoked when
-a :term:`exception view` is rendered successfully: in such a case, the
-:attr:`request.exception` attribute of the request when it enters a
-response callback will be an exception object instead of its default
-value of ``None``.
+callable` is invalid. Response callbacks *are*, however, invoked when a
+:term:`exception view` is rendered successfully: in such a case, the
+:attr:`request.exception` attribute of the request when it enters a response
+callback will be an exception object instead of its default value of
+``None``.
Response callbacks are called in the order they're added
(first-to-most-recently-added). All response callbacks are called *after*
@@ -532,18 +333,21 @@ response callback to happen as the result of *every* request, you must
re-register the callback into every new request (perhaps within a subscriber
of a :class:`pyramid.events.NewRequest` event).
+.. index::
+ single: finished callback
+
.. _using_finished_callbacks:
Using Finished Callbacks
------------------------
-A :term:`finished callback` is a function that will be called
-unconditionally by the :app:`Pyramid` :term:`router` at the very
-end of request processing. A finished callback can be used to perform
-an action at the end of a request unconditionally.
+A :term:`finished callback` is a function that will be called unconditionally
+by the :app:`Pyramid` :term:`router` at the very end of request processing.
+A finished callback can be used to perform an action at the end of a request
+unconditionally.
-The :meth:`pyramid.request.Request.add_finished_callback` method is
-used to register a finished callback.
+The :meth:`pyramid.request.Request.add_finished_callback` method is used to
+register a finished callback.
A finished callback is a callable which accepts a single positional
parameter: ``request``. For example:
@@ -563,25 +367,24 @@ parameter: ``request``. For example:
Finished callbacks are called in the order they're added ( first- to
most-recently- added). Finished callbacks (unlike a :term:`response
-callback`) are *always* called, even if an exception happens in
-application code that prevents a response from being generated.
-
-The set of finished callbacks associated with a request are called
-*very late* in the processing of that request; they are essentially
-the very last thing called by the :term:`router` before a request
-"ends". They are called after response processing has already occurred
-in a top-level ``finally:`` block within the router request processing
-code. As a result, mutations performed to the ``request`` provided to
-a finished callback will have no meaningful effect, because response
-processing will have already occurred, and the request's scope will
-expire almost immediately after all finished callbacks have been
-processed.
+callback`) are *always* called, even if an exception happens in application
+code that prevents a response from being generated.
+
+The set of finished callbacks associated with a request are called *very
+late* in the processing of that request; they are essentially the very last
+thing called by the :term:`router` before a request "ends". They are called
+after response processing has already occurred in a top-level ``finally:``
+block within the router request processing code. As a result, mutations
+performed to the ``request`` provided to a finished callback will have no
+meaningful effect, because response processing will have already occurred,
+and the request's scope will expire almost immediately after all finished
+callbacks have been processed.
It is often necessary to tell whether an exception occurred within
-:term:`view callable` code from within a finished callback: in such a
-case, the :attr:`request.exception` attribute of the request when it
-enters a response callback will be an exception object instead of its
-default value of ``None``.
+:term:`view callable` code from within a finished callback: in such a case,
+the :attr:`request.exception` attribute of the request when it enters a
+response callback will be an exception object instead of its default value of
+``None``.
Errors raised by finished callbacks are not handled specially. They
will be propagated to the caller of the :app:`Pyramid` router
@@ -592,28 +395,175 @@ finished callback to happen as the result of *every* request, you must
re-register the callback into every new request (perhaps within a subscriber
of a :class:`pyramid.events.NewRequest` event).
+.. index::
+ single: traverser
+
+.. _changing_the_traverser:
+
+Changing the Traverser
+----------------------
+
+The default :term:`traversal` algorithm that :app:`Pyramid` uses is explained
+in :ref:`traversal_algorithm`. Though it is rarely necessary, this default
+algorithm can be swapped out selectively for a different traversal pattern
+via configuration.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.interfaces import ITraverser
+ from zope.interface import Interface
+ from myapp.traversal import Traverser
+
+ config.registry.registerAdapter(Traverser, (Interface,), ITraverser)
+
+In the example above, ``myapp.traversal.Traverser`` is assumed to be a class
+that implements the following interface:
+
+.. code-block:: python
+ :linenos:
+
+ class Traverser(object):
+ def __init__(self, root):
+ """ Accept the root object returned from the root factory """
+
+ def __call__(self, request):
+ """ Return a dictionary with (at least) the keys ``root``,
+ ``context``, ``view_name``, ``subpath``, ``traversed``,
+ ``virtual_root``, and ``virtual_root_path``. These values are
+ typically the result of a resource tree traversal. ``root``
+ is the physical root object, ``context`` will be a resource
+ object, ``view_name`` will be the view name used (a Unicode
+ name), ``subpath`` will be a sequence of Unicode names that
+ followed the view name but were not traversed, ``traversed``
+ will be a sequence of Unicode names that were traversed
+ (including the virtual root path, if any) ``virtual_root``
+ will be a resource object representing the virtual root (or the
+ physical root if traversal was not performed), and
+ ``virtual_root_path`` will be a sequence representing the
+ virtual root path (a sequence of Unicode names) or None if
+ traversal was not performed.
+
+ Extra keys for special purpose functionality can be added as
+ necessary.
+
+ All values returned in the dictionary will be made available
+ as attributes of the ``request`` object.
+ """
+
+More than one traversal algorithm can be active at the same time. For
+instance, if your :term:`root factory` returns more than one type of object
+conditionally, you could claim that an alternate traverser adapter is ``for``
+only one particular class or interface. When the root factory returned an
+object that implemented that class or interface, a custom traverser would be
+used. Otherwise, the default traverser would be used. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.interfaces import ITraverser
+ from zope.interface import Interface
+ from myapp.traversal import Traverser
+ from myapp.resources import MyRoot
+
+ config.registry.registerAdapter(Traverser, (MyRoot,), ITraverser)
+
+If the above stanza was added to a Pyramid ``__init__.py`` file's ``main``
+function, :app:`Pyramid` would use the ``myapp.traversal.Traverser`` only
+when the application :term:`root factory` returned an instance of the
+``myapp.resources.MyRoot`` object. Otherwise it would use the default
+:app:`Pyramid` traverser to do traversal.
+
+For information about how to configure an alternate traverser via
+:term:`ZCML`, see :ref:`changing_traverser_zcml`.
+
+.. index::
+ single: url generator
+
+.. _changing_resource_url:
+
+Changing How :mod:`pyramid.url.resource_url` Generates a URL
+------------------------------------------------------------
+
+When you add a traverser as described in :ref:`changing_the_traverser`, it's
+often convenient to continue to use the :func:`pyramid.url.resource_url` API.
+However, since the way traversal is done will have been modified, the URLs it
+generates by default may be incorrect.
+
+If you've added a traverser, you can change how
+:func:`pyramid.url.resource_url` generates a URL for a specific type of
+resource by adding a registerAdapter call for
+:class:`pyramid.interfaces.IContextURL` to your application:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.interfaces import ITraverser
+ from zope.interface import Interface
+ from myapp.traversal import URLGenerator
+ from myapp.resources import MyRoot
+
+ config.registry.registerAdapter(URLGenerator, (MyRoot, Interface),
+ IContextURL)
+
+In the above example, the ``myapp.traversal.URLGenerator`` class will be used
+to provide services to :func:`pyramid.url.resource_url` any time the
+:term:`context` passed to ``resource_url`` is of class
+``myapp.resources.MyRoot``. The second argument in the ``(MyRoot,
+Interface)`` tuple represents the type of interface that must be possessed by
+the :term:`request` (in this case, any interface, represented by
+``zope.interface.Interface``).
+
+The API that must be implemented by a class that provides
+:class:`pyramid.interfaces.IContextURL` is as follows:
+
+.. code-block:: python
+ :linenos:
+
+ from zope.interface import Interface
+
+ class IContextURL(Interface):
+ """ An adapter which deals with URLs related to a context.
+ """
+ def __init__(self, context, request):
+ """ Accept the context and request """
+
+ def virtual_root(self):
+ """ Return the virtual root object related to a request and the
+ current context"""
+
+ def __call__(self):
+ """ Return a URL that points to the context """
+
+The default context URL generator is available for perusal as the class
+:class:`pyramid.traversal.TraversalContextURL` in the `traversal module
+<http://github.com/Pylons/pyramid/blob/master/pyramid/traversal.py>`_ of the
+:term:`Pylons` GitHub Pyramid repository.
+
+.. index::
+ single: configuration decorator
+
.. _registering_configuration_decorators:
Registering Configuration Decorators
------------------------------------
Decorators such as :class:`pyramid.view.view_config` don't change the
-behavior of the functions or classes they're decorating. Instead,
-when a :term:`scan` is performed, a modified version of the function
-or class is registered with :app:`Pyramid`.
-
-You may wish to have your own decorators that offer such
-behaviour. This is possible by using the :term:`Venusian` package in
-the same way that it is used by :app:`Pyramid`.
-
-By way of example, let's suppose you want to write a decorator that
-registers the function it wraps with a :term:`Zope Component
-Architecture` "utility" within the :term:`application registry`
-provided by :app:`Pyramid`. The application registry and the
-utility inside the registry is likely only to be available once your
-application's configuration is at least partially completed. A normal
-decorator would fail as it would be executed before the configuration
-had even begun.
+behavior of the functions or classes they're decorating. Instead, when a
+:term:`scan` is performed, a modified version of the function or class is
+registered with :app:`Pyramid`.
+
+You may wish to have your own decorators that offer such behaviour. This is
+possible by using the :term:`Venusian` package in the same way that it is
+used by :app:`Pyramid`.
+
+By way of example, let's suppose you want to write a decorator that registers
+the function it wraps with a :term:`Zope Component Architecture` "utility"
+within the :term:`application registry` provided by :app:`Pyramid`. The
+application registry and the utility inside the registry is likely only to be
+available once your application's configuration is at least partially
+completed. A normal decorator would fail as it would be executed before the
+configuration had even begun.
However, using :term:`Venusian`, the decorator could be written as
follows:
@@ -671,10 +621,8 @@ performed, enabling you to set up the utility in advance:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.registry.registerUtility(UtilityImplementation())
config.scan()
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst
index d8cc5cb1c..c2a5b8ce7 100644
--- a/docs/narr/i18n.rst
+++ b/docs/narr/i18n.rst
@@ -881,11 +881,8 @@ application startup. For example:
:linenos:
from pyramid.config import Configurator
- config.begin()
config.add_translation_dirs('my.application:locale/',
'another.application:locale/')
- # ...
- config.end()
A message catalog in a translation directory added via
:meth:`pyramid.config.Configurator.add_translation_dirs`
@@ -1020,9 +1017,7 @@ For example:
from pyramid.config import Configurator
config = Configurator()
- config.begin()
config.set_locale_negotiator(my_locale_negotiator)
- config.end()
.. note:: You can also add a custom locale negotiator via ZCML. See
:ref:`zcml_adding_a_locale_negotiator`
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index a096d8ca4..36f2d6975 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -64,8 +64,7 @@ The included templates are these:
:term:`ZCML` (declarative configuration).
``pyramid_zodb``
- URL mapping via :term:`traversal` and persistence via :term:`ZODB`, using
- :term:`ZCML` (declarative configuration).
+ URL mapping via :term:`traversal` and persistence via :term:`ZODB`.
``pyramid_routesalchemy``
URL mapping via :term:`URL dispatch` and persistence via
diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst
index 4baa39bd3..3b9efe108 100644
--- a/docs/narr/resources.rst
+++ b/docs/narr/resources.rst
@@ -324,6 +324,11 @@ would have been what was returned anyway, but your code can perform arbitrary
logic as necessary. For example, your code may wish to override the hostname
or port number of the generated URL.
+Note that the URL generated by ``__resource_url__`` should be fully
+qualified, should end in a slash, and should not contain any query string or
+anchor elements (only path elements) to work best with
+:func:`pyramid.url.resource_url`.
+
Generating the Path To a Resource
---------------------------------
diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst
index 7a0a03384..de9add3b7 100644
--- a/docs/narr/sessions.rst
+++ b/docs/narr/sessions.rst
@@ -10,6 +10,8 @@ A :term:`session` is a namespace which is valid for some period of
continual activity that can be used to represent a user's interaction
with a web application.
+.. _using_the_default_session_factory:
+
Using The Default Session Factory
---------------------------------
@@ -131,6 +133,8 @@ Some gotchas:
single: pyramid_beaker
single: Beaker
+.. _using_alternate_session_factories:
+
Using Alternate Session Factories
---------------------------------
diff --git a/docs/narr/static.rst b/docs/narr/static.rst
deleted file mode 100644
index 6b03b2349..000000000
--- a/docs/narr/static.rst
+++ /dev/null
@@ -1,277 +0,0 @@
-Static Assets
-=============
-
-:app:`Pyramid` makes it possible to serve up static asset files from a
-directory on a filesystem. This chapter describes how to configure
-:app:`Pyramid` to do so.
-
-.. index::
- single: add_static_view
-
-.. _static_assets_section:
-
-Serving Static Assets
----------------------
-
-Use the :meth:`pyramid.config.Configurator.add_static_view` to instruct
-:app:`Pyramid` to serve static assets such as JavaScript and CSS files. This
-mechanism makes static files available at a name relative to the application
-root URL, e.g. ``/static``.
-
-Note that the ``path`` provided to
-:meth:`pyramid.config.Configurator.add_static_view` may be a fully qualified
-:term:`asset specification`, or an *absolute path*.
-
-Here's an example of a use of
-:meth:`pyramid.config.Configurator.add_static_view` that will serve
-files up under the ``/static`` URL from the ``/var/www/static`` directory of
-the computer which runs the :app:`Pyramid` application using an absolute
-path.
-
-.. code-block:: python
- :linenos:
-
- # config is an instance of pyramid.config.Configurator
- config.add_static_view(name='static', path='/var/www/static')
-
-Here's an example of :meth:`pyramid.config.Configurator.add_static_view` that
-will serve files up under the ``/static`` URL from the ``a/b/c/static``
-directory of the Python package named ``some_package`` using a fully
-qualified :term:`asset specification`.
-
-.. code-block:: python
- :linenos:
-
- # config is an instance of pyramid.config.Configurator
- config.add_static_view(name='static', path='some_package:a/b/c/static')
-
-Whether you use for ``path`` a fully qualified asset specification, or an
-absolute path, when you place your static files on the filesystem in the
-directory represented as the ``path`` of the directive, you will then be able
-to view the static files in this directory via a browser at URLs prefixed
-with the directive's ``name``. For instance if the ``static`` directive's
-``name`` is ``static`` and the static directive's ``path`` is
-``/path/to/static``, ``http://localhost:6543/static/foo.js`` will return the
-file ``/path/to/static/dir/foo.js``. The static directory may contain
-subdirectories recursively, and any subdirectories may hold files; these will
-be resolved by the static view as you would expect.
-
-While the ``path`` argument can be a number of different things, the ``name``
-argument of the call to :meth:`pyramid.config.Configurator.add_static_view`
-can also be one of a number of things: a *view name* or a *URL*. The above
-examples have shown usage of the ``name`` argument as a view name. When
-``name`` is a *URL* (or any string with a slash (``/``) in it), static assets
-can be served from an external webserver. In this mode, the ``name`` is used
-as the URL prefix when generating a URL using :func:`pyramid.url.static_url`.
-
-.. note::
-
- Using :func:`pyramid.url.static_url` in conjunction with a
- :meth:`pyramid.config.Configurator.add_static_view` makes it
- possible to put static media on a separate webserver during production (if
- the ``name`` argument to
- :meth:`pyramid.config.Configurator.add_static_view` is a URL),
- while keeping static media package-internal and served by the development
- webserver during development (if the ``name`` argument to
- :meth:`pyramid.config.Configurator.add_static_view` is a view
- name). To create such a circumstance, we suggest using the
- :attr:`pyramid.registry.Registry.settings` API in conjunction with a
- setting in the application ``.ini`` file named ``media_location``. Then
- set the value of ``media_location`` to either a view name or a URL
- depending on whether the application is being run in development or in
- production (use a different `.ini`` file for production than you do for
- development). This is just a suggestion for a pattern; any setting name
- other than ``media_location`` could be used.
-
-For example, :meth:`pyramid.config.Configurator.add_static_view` may
-be fed a ``name`` argument which is ``http://example.com/images``:
-
-.. code-block:: python
- :linenos:
-
- # config is an instance of pyramid.config.Configurator
- config.add_static_view(name='http://example.com/images',
- path='mypackage:images')
-
-Because :meth:`pyramid.config.Configurator.add_static_view` is
-provided with a ``name`` argument that is the URL prefix
-``http://example.com/images``, subsequent calls to
-:func:`pyramid.url.static_url` with paths that start with the ``path``
-argument passed to :meth:`pyramid.config.Configurator.add_static_view`
-will generate a URL something like ``http://example.com/images/logo.png``. The
-external webserver listening on ``example.com`` must be itself configured to
-respond properly to such a request. The :func:`pyramid.url.static_url` API
-is discussed in more detail later in this chapter.
-
-The :ref:`static_directive` ZCML directive offers an declarative equivalent
-to :meth:`pyramid.config.Configurator.add_static_view`. Use of the
-:ref:`static_directive` ZCML directive is completely equivalent to using
-imperative configuration for the same purpose.
-
-.. note::
-
- Using :func:`pyramid.url.static_url` in conjunction with a
- :meth:`pyramid.configuration.Configurator.add_static_view` makes it
- possible to put static media on a separate webserver during production (if
- the ``name`` argument to
- :meth:`pyramid.configuration.Configurator.add_static_view` is a URL),
- while keeping static media package-internal and served by the development
- webserver during development (if the ``name`` argument to
- :meth:`pyramid.configuration.Configurator.add_static_view` is a view
- name). To create such a circumstance, we suggest using the
- :attr:`pyramid.registry.Registry.settings` API in conjunction with a
- setting in the application ``.ini`` file named ``media_location``. Then
- set the value of ``media_location`` to either a view name or a URL
- depending on whether the application is being run in development or in
- production (use a different `.ini`` file for production than you do for
- development). This is just a suggestion for a pattern; any setting name
- other than ``media_location`` could be used.
-
-.. index::
- single: generating static asset urls
- single: static asset urls
-
-.. _generating_static_asset_urls:
-
-Generating Static Asset URLs
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When a :meth:`pyramid.config.Configurator.add_static_view` method is used to
-register a static asset directory, a special helper API named
-:func:`pyramid.url.static_url` can be used to generate the appropriate URL
-for an asset that lives in one of the directories named by the static
-registration ``path`` attribute.
-
-For example, let's assume you create a set of static declarations like so:
-
-.. code-block:: python
- :linenos:
-
- config.add_static_view(name='static1', path='mypackage:assets/1')
- config.add_static_view(name='static2', path='mypackage:assets/2')
-
-These declarations create URL-accessible directories which have URLs that
-begin with ``/static1`` and ``/static2``, respectively. The assets in the
-``assets/1`` directory of the ``mypackage`` package are consulted when a user
-visits a URL which begins with ``/static1``, and the assets in the
-``assets/2`` directory of the ``mypackage`` package are consulted when a user
-visits a URL which begins with ``/static2``.
-
-You needn't generate the URLs to static assets "by hand" in such a
-configuration. Instead, use the :func:`pyramid.url.static_url` API to
-generate them for you. For example:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.url import static_url
- from pyramid.chameleon_zpt import render_template_to_response
-
- def my_view(request):
- css_url = static_url('mypackage:assets/1/foo.css', request)
- js_url = static_url('mypackage:assets/2/foo.js', request)
- return render_template_to_response('templates/my_template.pt',
- css_url = css_url,
- js_url = js_url)
-
-If the request "application URL" of the running system is
-``http://example.com``, the ``css_url`` generated above would be:
-``http://example.com/static1/foo.css``. The ``js_url`` generated
-above would be ``http://example.com/static2/foo.js``.
-
-One benefit of using the :func:`pyramid.url.static_url` function rather than
-constructing static URLs "by hand" is that if you need to change the ``name``
-of a static URL declaration, the generated URLs will continue to resolve
-properly after the rename.
-
-URLs may also be generated by :func:`pyramid.url.static_url` to static assets
-that live *outside* the :app:`Pyramid` application. This will happen when
-the :meth:`pyramid.config.Configurator.add_static_view` API associated with
-the path fed to :func:`pyramid.url.static_url` is a *URL* instead of a view
-name. For example, the ``name`` argument may be ``http://example.com`` while
-the the ``path`` given may be ``mypackage:images``:
-
-.. code-block:: python
- :linenos:
-
- config.add_static_view(name='http://example.com/images', path='mypackage:images')
-
-Under such a configuration, the URL generated by ``static_url`` for
-assets which begin with ``mypackage:images`` will be prefixed with
-``http://example.com/images``:
-
-.. code-block:: python
- :linenos:
-
- static_url('mypackage:images/logo.png', request)
- # -> http://example.com/images/logo.png
-
-.. index::
- single: static assets view
-
-Advanced: Serving Static Assets Using a View Callable
------------------------------------------------------
-
-For more flexibility, static assets can be served by a :term:`view callable`
-which you register manually. For example, you may want static assets to only
-be available when the :term:`context` is of a particular type, or when
-certain request headers are present.
-
-The :class:`pyramid.view.static` helper class is used to perform this
-task. This class creates an object that is capable acting as a :app:`Pyramid`
-view callable which serves static assets from a directory. For instance, to
-serve files within a directory located on your filesystem at
-``/path/to/static/dir`` from the URL path ``/static`` in your application,
-create an instance of the :class:`pyramid.view.static` class inside a
-``static.py`` file in your application root as below.
-
-.. ignore-next-block
-.. code-block:: python
- :linenos:
-
- from pyramid.view import static
- static_view = static('/path/to/static/dir')
-
-.. note:: the argument to :class:`pyramid.view.static` can also be
- a "here-relative" pathname, e.g. ``my/static`` (meaning relative to the
- Python package of the module in which the view is being defined).
- It can also be a :term:`asset specification`
- (e.g. ``anotherpackage:some/subdirectory``).
-
-Subsequently, you may wire this view up to be accessible as ``/static`` using
-the :mod:`pyramid.config.Configurator.add_view` method in your application's
-startup code against either the class or interface that represents your root
-resource object.
-
-.. code-block:: python
- :linenos:
-
- config.add_view('mypackage.static.static_view', name='static',
- context='mypackage.resources.Root')
-
-In this case, ``mypackage.resources.Root`` refers to the class of your
-:app:`Pyramid` application's resource tree.
-
-The context argument above limits where the static view is accessible to URL
-paths directly under the root object. If you omit the ``context`` argument,
-then ``static`` will be accessible as the static view against any resource
-object in the resource tree. This will allow ``/static/foo.js`` to work, but
-it will also allow for ``/anything/static/foo.js`` too, as long as
-``anything`` can be resolved.
-
-Note that you cannot use the :func:`pyramid.url.static_url` API to generate
-URLs against assets made accessible by registering a custom static view.
-
-.. warning::
-
- When adding a static view to your root object, you need to be careful that
- there are no resource objects contained in the root with the same key as
- the view name (e.g., ``static``). Resource objects take precedence during
- traversal, thus such a name collision will cause the resource to "shadow"
- your static view. To avoid this issue, and ensure that your root
- resource's ``__getitem__`` is never called when a static asset is
- requested, you can refer to them unambiguously using the ``@@`` prefix
- (goggles) in their URLs. For the above examples you could use
- '/@@static/foo.js' instead of '/static/foo.js' to avoid such shadowing.
- See :ref:`traversal_chapter` for information about "goggles" (``@@``).
-
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index 155ece8df..ad28e48d4 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -3,7 +3,7 @@
Views
=====
-One of the primary jobs of :app:`Pyramid` is is to find and invoke a
+One of the primary jobs of :app:`Pyramid` is to find and invoke a
:term:`view callable` when a :term:`request` reaches your application. View
callables are bits of code which do something interesting in response to a
request made to your application.