diff options
| -rw-r--r-- | docs/index.rst | 1 | ||||
| -rw-r--r-- | docs/latexindex.rst | 1 | ||||
| -rw-r--r-- | docs/narr/configuration.rst | 317 | ||||
| -rw-r--r-- | docs/narr/declarative.rst | 852 | ||||
| -rw-r--r-- | docs/narr/extending.rst | 10 | ||||
| -rw-r--r-- | docs/narr/firstapp.rst | 291 | ||||
| -rw-r--r-- | docs/narr/hybrid.rst | 314 | ||||
| -rw-r--r-- | docs/narr/project.rst | 21 | ||||
| -rw-r--r-- | docs/narr/security.rst | 211 | ||||
| -rw-r--r-- | docs/narr/startup.rst | 31 | ||||
| -rw-r--r-- | docs/narr/static.rst | 272 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 302 |
12 files changed, 1276 insertions, 1347 deletions
diff --git a/docs/index.rst b/docs/index.rst index 79440448b..5a9aab565 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -51,6 +51,7 @@ Narrative documentation in chapter form explaining how to use narr/environment narr/unittesting narr/hooks + narr/declarative narr/extending narr/resources narr/router diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 52eb8bd96..93d98d14c 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -50,6 +50,7 @@ Narrative Documentation narr/environment narr/unittesting narr/hooks + narr/declarative narr/extending narr/resources narr/router diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst index cbc49d94b..82d0c8dda 100644 --- a/docs/narr/configuration.rst +++ b/docs/narr/configuration.rst @@ -6,30 +6,30 @@ Application Configuration ========================= -Each deployment of an application written using :mod:`pyramid` -implies a specific *configuration* of the framework itself. For -example, an application which serves up MP3s for user consumption -might plug code into the framework that manages songs, while an -application that manages corporate data might plug in code that -manages accounting information. :mod:`pyramid` refers to the way -in which code is plugged in to it for a specific application as -"configuration". - -Most people understand "configuration" as coarse settings that inform -the high-level operation of a specific application deployment. For -instance, it's easy to think of the values implied by a ``.ini`` file -parsed at application startup time as "configuration". -:mod:`pyramid` extends this pattern to application development, -using the term "configuration" to express standardized ways that code -gets plugged into a deployment of the framework itself. When you plug -code into the :mod:`pyramid` framework, you are "configuring" -:mod:`pyramid` for the purpose of creating a particular application -deployment. - -There are two different mechanisms you may use to configure -:mod:`pyramid` to create an application: *imperative* configuration -and *declarative* configuration. We'll examine both modes in the -sections which follow. +Each deployment of an application written using :mod:`pyramid` implies a +specific *configuration* of the framework itself. For example, an +application which serves up MP3s for user consumption might plug code into +the framework that manages songs, while an application that manages corporate +data might plug in code that manages accounting information. :mod:`pyramid` +refers to the way in which code is plugged in to it for a specific +application as "configuration". + +Most people understand "configuration" as coarse settings that inform the +high-level operation of a specific application deployment. For instance, +it's easy to think of the values implied by a ``.ini`` file parsed at +application startup time as "configuration". :mod:`pyramid` extends this +pattern to application development, using the term "configuration" to express +standardized ways that code gets plugged into a deployment of the framework +itself. When you plug code into the :mod:`pyramid` framework, you are +"configuring" :mod:`pyramid` for the purpose of creating a particular +application deployment. + +There are two different mechanisms you may use to configure :mod:`pyramid` to +create an application: *imperative* configuration and *declarative* +configuration. We'll be using primarily the *imperative* configuration +method in this book, for the sake of brevity and simplicity. For information +about how to define application configuration declaratively, see +:ref:`declarative_chapter`. .. index:: single: imperative configuration @@ -39,13 +39,6 @@ sections which follow. Imperative Configuration ------------------------ -Experienced Python programmers might find that performing -configuration "imperatively" fits their brain best. 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. - Here's one of the simplest :mod:`pyramid` applications, configured imperatively: @@ -77,201 +70,22 @@ power of Python, including conditionals, can be employed in this mode of configuration. .. index:: - single: declarative configuration - -.. _declarative_configuration: - -Declarative Configuration -------------------------- - -A :mod:`pyramid` application can be alternately be configured -"declaratively", if so desired. Declarative configuration relies on -*declarations* made external to the code in a configuration file -format named :term:`ZCML` (Zope Configuration Markup Language), an XML -dialect. - -A :mod:`pyramid` application configured declaratively requires not -one, but two files: a Python file and a :term:`ZCML` file. - -In a file named ``helloworld.py``: - -.. code-block:: python - :linenos: - - from paste.httpserver import serve - from pyramid.response import Response - from pyramid.configuration import Configurator - - def hello_world(request): - return Response('Hello world!') - - 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') - -In a file named ``configure.zcml`` in the same directory as the -previously created ``helloworld.py``: - -.. code-block:: xml - :linenos: - - <configure xmlns="http://pylonshq.com/pyramid"> - - <include package="pyramid.includes" /> - - <view - view="helloworld.hello_world" - /> - - </configure> - -This pair of files forms an application functionally equivalent to the -application we created earlier in :ref:`imperative_configuration`. -Let's examine the differences between that code listing and the code -above. - -In :ref:`imperative_configuration`, we had the following lines within -the ``if __name__ == '__main__'`` section of ``helloworld.py``: - -.. code-block:: python - :linenos: - - 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') - -In our "declarative" code, we've removed the call to ``add_view`` and -replaced it with a call to the -:meth:`pyramid.configuration.Configurator.load_zcml` method so that -it now reads as: - -.. code-block:: python - :linenos: - - 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') - -Everything else is much the same. - -The ``config.load_zcml('configure.zcml')`` line tells the configurator -to load configuration declarations from the file named -``configure.zcml`` which sits next to ``helloworld.py`` on the -filesystem. Let's take a look at that ``configure.zcml`` file again: - -.. code-block:: xml - :linenos: - - <configure xmlns="http://pylonshq.com/pyramid"> - - <include package="pyramid.includes" /> - - <view - view="helloworld.hello_world" - /> - - </configure> - -Note that this file contains some XML, and that the XML contains a -``<view>`` :term:`configuration declaration` tag that references a -:term:`dotted Python name`. This dotted name refers to the -``hello_world`` function that lives in our ``helloworld`` Python -module. - -This ``<view>`` declaration tag performs the same function as the -``add_view`` method that was employed within -:ref:`imperative_configuration`. In fact, the ``<view>`` tag is -effectively a "macro" which calls the -:meth:`pyramid.configuration.Configurator.add_view` method on your -behalf. - -The ``<view>`` tag is an example of a :mod:`pyramid` declaration -tag. Other such tags include ``<route>`` and ``<scan>``. Each of -these tags is effectively a "macro" which calls methods of a -:class:`pyramid.configuration.Configurator` object on your behalf. - -Essentially, using a :term:`ZCML` file and loading it from the -filesystem allows us to put our configuration statements within this -XML file rather as declarations, rather than representing them as -method calls to a :term:`Configurator` object. Otherwise, declarative -and imperative configuration are functionally equivalent. - -Using declarative configuration has a number of benefits, the primary -benefit being that applications configured declaratively can be -*overridden* and *extended* by third parties without requiring the -third party to change application code. If you want to build a -framework or an extensible application, using declarative -configuration is a good idea. - -Declarative configuration has an obvious downside: you can't use -plain-old-Python syntax you probably already know and understand to -configure your application; instead you need to use :term:`ZCML`. - -.. index:: - single: ZCML conflict detection - -ZCML Conflict Detection -~~~~~~~~~~~~~~~~~~~~~~~ - -A minor additional feature of ZCML is *conflict detection*. If you -define two declaration tags within the same ZCML file which logically -"collide", an exception will be raised, and the application will not -start. For example, the following ZCML file has two conflicting -``<view>`` tags: - -.. code-block:: xml - :linenos: - - <configure xmlns="http://pylonshq.com/pyramid"> - - <include package="pyramid.includes" /> - - <view - view="helloworld.hello_world" - /> - - <view - view="helloworld.hello_world" - /> - - </configure> - -If you try to use this ZCML file as the source of ZCML for an -application, an error will be raised when you attempt to start the -application. This error will contain information about which tags -might have conflicted. - -.. index:: single: view_config - single: ZCML view directive single: configuration decoration single: code scanning .. _decorations_and_code_scanning: Configuration Decorations and Code Scanning -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An alternate mode of declarative configuration lends more *locality of -reference* to a :term:`configuration declaration`. It's sometimes -painful to have all configuration done in ZCML, or even in imperative -code, because you may need to have two files open at once to see the -"big picture": the file that represents the configuration, and the -file that contains the implementation objects referenced by the -configuration. To avoid this, :mod:`pyramid` allows you to insert -:term:`configuration decoration` statements very close to code that is +------------------------------------------- + +An alternate mode of configuration lends more *locality of reference* to a +:term:`configuration declaration`. It's sometimes painful to have all +configuration done in imperative code, because you may need to have two files +open at once to see the "big picture": the file that represents the +configuration, and the file that contains the implementation objects +referenced by the configuration. To avoid this, :mod:`pyramid` allows you to +insert :term:`configuration decoration` statements very close to code that is referred to by the declaration itself. For example: .. code-block:: python @@ -322,46 +136,6 @@ and its subpackages. For example: app = config.make_wsgi_app() serve(app, host='0.0.0.0') -:term:`ZCML` can also invoke a :term:`scan` via its ``<scan>`` -directive. If a ZCML file is processed that contains a scan -directive, the package the ZCML file points to is scanned. - -.. topic:: Declaratively Starting a Scan - - .. code-block:: python - :linenos: - - # helloworld.py - - from paste.httpserver import serve - from pyramid.response import Response - from pyramid.view import view_config - - @view_config() - def hello(request): - return Response('Hello') - - if __name__ == '__main__': - from pyramid.configuration 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') - - .. code-block:: xml - :linenos: - - <configure xmlns="http://namespaces.repoze.org"> - - <!-- configure.zcml --> - - <include package="pyramid.includes"/> - <scan package="."/> - - </configure> - The scanning machinery imports each module and subpackage in a package or module recursively, looking for special attributes attached to objects defined within a module. These special attributes are @@ -384,20 +158,15 @@ method, effectively: config.add_view(hello) -Which Mode Should I Use? ------------------------- - -A combination of imperative configuration, declarative configuration -via ZCML and scanning can be used to configure any application. They -are not mutually exclusive. +Declarative Configuration +------------------------- -The :mod:`pyramid` authors often recommend using mostly declarative -configuration, because it's the more traditional form of configuration -used in :mod:`pyramid` applications, it can be overridden and -extended by third party deployers, and there are more examples for it -"in the wild". +A third mode of configuration can be employed when you create a +:mod:`pyramid` application named *declarative configuration*. This mode uses +:term:`ZCML` to represent configuration statements rather than Python. ZCML +is often used when application extensibility is important. Most of the +examples in the narrative portion of this documentation concentrate on +imperative configuration rather than ZCML, but almost everything that can be +configured imperatively can also be configured via ZCML. See +:ref:`declarative_chapter` for more information about ZCML. -However, imperative mode configuration can be simpler to understand, -and the framework is not "opinionated" about the choice. This book -presents examples in both styles, mostly interchangeably. You can -choose the mode that best fits your brain as necessary. diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst new file mode 100644 index 000000000..33fd89073 --- /dev/null +++ b/docs/narr/declarative.rst @@ -0,0 +1,852 @@ +.. _declarative_chapter: + +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 :mod:`pyramid`, which often provides better +extensibility and configuration conflict detection. + +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`. + +.. index:: + single: declarative configuration + +.. _declarative_configuration: + +Declarative Configuration +------------------------- + +A :mod:`pyramid` application can be configured "declaratively", if so +desired. Declarative configuration relies on *declarations* made external to +the code in a configuration file format named :term:`ZCML` (Zope +Configuration Markup Language), an XML dialect. + +A :mod:`pyramid` application configured declaratively requires not +one, but two files: a Python file and a :term:`ZCML` file. + +In a file named ``helloworld.py``: + +.. code-block:: python + :linenos: + + from paste.httpserver import serve + from pyramid.response import Response + from pyramid.configuration import Configurator + + def hello_world(request): + return Response('Hello world!') + + 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') + +In a file named ``configure.zcml`` in the same directory as the +previously created ``helloworld.py``: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://pylonshq.com/pyramid"> + + <include package="pyramid.includes" /> + + <view + view="helloworld.hello_world" + /> + + </configure> + +This pair of files forms an application functionally equivalent to the +application we created earlier in :ref:`imperative_configuration`. +Let's examine the differences between that code listing and the code +above. + +In :ref:`imperative_configuration`, we had the following lines within +the ``if __name__ == '__main__'`` section of ``helloworld.py``: + +.. code-block:: python + :linenos: + + 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') + +In our "declarative" code, we've removed the call to ``add_view`` and +replaced it with a call to the +:meth:`pyramid.configuration.Configurator.load_zcml` method so that +it now reads as: + +.. code-block:: python + :linenos: + + 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') + +Everything else is much the same. + +The ``config.load_zcml('configure.zcml')`` line tells the configurator +to load configuration declarations from the file named +``configure.zcml`` which sits next to ``helloworld.py`` on the +filesystem. Let's take a look at that ``configure.zcml`` file again: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://pylonshq.com/pyramid"> + + <include package="pyramid.includes" /> + + <view + view="helloworld.hello_world" + /> + + </configure> + +Note that this file contains some XML, and that the XML contains a +``<view>`` :term:`configuration declaration` tag that references a +:term:`dotted Python name`. This dotted name refers to the +``hello_world`` function that lives in our ``helloworld`` Python +module. + +This ``<view>`` declaration tag performs the same function as the +``add_view`` method that was employed within +:ref:`imperative_configuration`. In fact, the ``<view>`` tag is +effectively a "macro" which calls the +:meth:`pyramid.configuration.Configurator.add_view` method on your +behalf. + +The ``<view>`` tag is an example of a :mod:`pyramid` declaration +tag. Other such tags include ``<route>`` and ``<scan>``. Each of +these tags is effectively a "macro" which calls methods of a +:class:`pyramid.configuration.Configurator` object on your behalf. + +Essentially, using a :term:`ZCML` file and loading it from the +filesystem allows us to put our configuration statements within this +XML file rather as declarations, rather than representing them as +method calls to a :term:`Configurator` object. Otherwise, declarative +and imperative configuration are functionally equivalent. + +Using declarative configuration has a number of benefits, the primary +benefit being that applications configured declaratively can be +*overridden* and *extended* by third parties without requiring the +third party to change application code. If you want to build a +framework or an extensible application, using declarative +configuration is a good idea. + +Declarative configuration has an obvious downside: you can't use +plain-old-Python syntax you probably already know and understand to +configure your application; instead you need to use :term:`ZCML`. + +.. index:: + single: ZCML conflict detection + +ZCML Conflict Detection +~~~~~~~~~~~~~~~~~~~~~~~ + +A minor additional feature of ZCML is *conflict detection*. If you +define two declaration tags within the same ZCML file which logically +"collide", an exception will be raised, and the application will not +start. For example, the following ZCML file has two conflicting +``<view>`` tags: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://pylonshq.com/pyramid"> + + <include package="pyramid.includes" /> + + <view + view="helloworld.hello_world" + /> + + <view + view="helloworld.hello_world" + /> + + </configure> + +If you try to use this ZCML file as the source of ZCML for an +application, an error will be raised when you attempt to start the +application. This error will contain information about which tags +might have conflicted. + +.. index:: + single: helloworld (declarative) + +.. _helloworld_declarative: + +Hello World, Goodbye World (Declarative) +---------------------------------------- + +Another almost entirely equivalent mode of application configuration +exists named *declarative* configuration. :mod:`pyramid` can be +configured for the same "hello world" application "declaratively", if +so desired. + +To do so, first, create a file named ``helloworld.py``: + +.. code-block:: python + :linenos: + + from pyramid.configuration import Configurator + from pyramid.response import Response + from paste.httpserver import serve + + def hello_world(request): + return Response('Hello world!') + + def goodbye_world(request): + return Response('Goodbye world!') + + 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') + +Then create a file named ``configure.zcml`` in the same directory as +the previously created ``helloworld.py``: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://pylonshq.com/pyramid"> + + <include package="pyramid.includes" /> + + <view + view="helloworld.hello_world" + /> + + <view + name="goodbye" + view="helloworld.goodbye_world" + /> + + </configure> + +This pair of files forms an application functionally equivalent to the +application we created earlier in :ref:`helloworld_imperative`. We can run +it the same way. + +.. code-block:: bash + + $ python helloworld.py + serving on 0.0.0.0:8080 view at http://127.0.0.1:8080 + +Let's examine the differences between the code in that section and the code +above. In :ref:`helloworld_imperative_appconfig`, we had the following lines +within the ``if __name__ == '__main__'`` section of ``helloworld.py``: + +.. code-block:: python + :linenos: + + 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') + +In our "declarative" code, we've added a call to the +:meth:`pyramid.configuration.Configurator.load_zcml` method with +the value ``configure.zcml``, and we've removed the lines which read +``config.add_view(hello_world)`` and ``config.add_view(goodbye_world, +name='goodbye')``, so that it now reads as: + +.. code-block:: python + :linenos: + + 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') + +Everything else is much the same. + +The ``config.load_zcml('configure.zcml')`` line tells the configurator +to load configuration declarations from the ``configure.zcml`` file +which sits next to ``helloworld.py``. Let's take a look at the +``configure.zcml`` file now: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://pylonshq.com/pyramid"> + + <include package="pyramid.includes" /> + + <view + view="helloworld.hello_world" + /> + + <view + name="goodbye" + view="helloworld.goodbye_world" + /> + + </configure> + +We already understand what the view code does, because the application +is functionally equivalent to the application described in +:ref:`helloworld_imperative`, but use of :term:`ZCML` is new. Let's +break that down tag-by-tag. + +The ``<configure>`` Tag +~~~~~~~~~~~~~~~~~~~~~~~ + +The ``configure.zcml`` ZCML file contains this bit of XML: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://pylonshq.com/pyramid"> + + <!-- other directives --> + + </configure> + +Because :term:`ZCML` is XML, and because XML requires a single root +tag for each document, every ZCML file used by :mod:`pyramid` must +contain a ``configure`` container directive, which acts as the root +XML tag. It is a "container" directive because its only job is to +contain other directives. + +See also :ref:`configure_directive` and :ref:`word_on_xml_namespaces`. + +The ``<include>`` Tag +~~~~~~~~~~~~~~~~~~~~~ + +The ``configure.zcml`` ZCML file contains this bit of XML within the +``<configure>`` root tag: + +.. code-block:: xml + + <include package="pyramid.includes" /> + +This self-closing tag instructs :mod:`pyramid` to load a ZCML file +from the Python package with the :term:`dotted Python name` +``pyramid.includes``, as specified by its ``package`` attribute. +This particular ``<include>`` declaration is required because it +actually allows subsequent declaration tags (such as ``<view>``, which +we'll see shortly) to be recognized. The ``<include>`` tag +effectively just includes another ZCML file, causing its declarations +to be executed. In this case, we want to load the declarations from +the file named ``configure.zcml`` within the +:mod:`pyramid.includes` Python package. We know we want to load +the ``configure.zcml`` from this package because ``configure.zcml`` is +the default value for another attribute of the ``<include>`` tag named +``file``. We could have spelled the include tag more verbosely, but +equivalently as: + +.. code-block:: xml + :linenos: + + <include package="pyramid.includes" + file="configure.zcml"/> + +The ``<include>`` tag that includes the ZCML statements implied by the +``configure.zcml`` file from the Python package named +:mod:`pyramid.includes` is basically required to come before any +other named declaration in an application's ``configure.zcml``. If it +is not included, subsequent declaration tags will fail to be +recognized, and the configuration system will generate an error at +startup. However, the ``<include package="pyramid.includes"/>`` +tag needs to exist only in a "top-level" ZCML file, it needn't also +exist in ZCML files *included by* a top-level ZCML file. + +See also :ref:`include_directive`. + +The ``<view>`` Tag +~~~~~~~~~~~~~~~~~~ + +The ``configure.zcml`` ZCML file contains these bits of XML *after* the +``<include>`` tag, but *within* the ``<configure>`` root tag: + +.. code-block:: xml + :linenos: + + <view + view="helloworld.hello_world" + /> + + <view + name="goodbye" + view="helloworld.goodbye_world" + /> + +These ``<view>`` declaration tags direct :mod:`pyramid` to create +two :term:`view configuration` registrations. The first ``<view>`` +tag has an attribute (the attribute is also named ``view``), which +points at a :term:`dotted Python name`, referencing the +``hello_world`` function defined within the ``helloworld`` package. +The second ``<view>`` tag has a ``view`` attribute which points at a +:term:`dotted Python name`, referencing the ``goodbye_world`` function +defined within the ``helloworld`` package. The second ``<view>`` tag +also has an attribute called ``name`` with a value of ``goodbye``. + +These effect of the ``<view>`` tag declarations we've put into our +``configure.zcml`` is functionally equivalent to the effect of lines +we've already seen in an imperatively-configured application. We're +just spelling things differently, using XML instead of Python. + +In our previously defined application, in which we added view +configurations imperatively, we saw this code: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.add_view(hello_world) + config.add_view(goodbye_world, name='goodbye') + +Each ``<view>`` declaration tag encountered in a ZCML file effectively +invokes the :meth:`pyramid.configuration.Configurator.add_view` +method on the behalf of the developer. Various attributes can be +specified on the ``<view>`` tag which influence the :term:`view +configuration` it creates. + +Since the relative ordering of calls to +:meth:`pyramid.configuration.Configurator.add_view` doesn't matter +(see the sidebar entitled *View Dispatch and Ordering* within +:ref:`adding_configuration`), the relative order of ``<view>`` tags in +ZCML doesn't matter either. The following ZCML orderings are +completely equivalent: + +.. topic:: Hello Before Goodbye + + .. code-block:: xml + :linenos: + + <view + view="helloworld.hello_world" + /> + + <view + name="goodbye" + view="helloworld.goodbye_world" + /> + +.. topic:: Goodbye Before Hello + + .. code-block:: xml + :linenos: + + <view + name="goodbye" + view="helloworld.goodbye_world" + /> + + <view + view="helloworld.hello_world" + /> + +We've now configured a :mod:`pyramid` helloworld application +declaratively. More information about this mode of configuration is +available in :ref:`declarative_configuration` and within +:ref:`zcml_reference`. + +Scanning +-------- + +:term:`ZCML` can also invoke a :term:`scan` via its ``<scan>`` +directive. If a ZCML file is processed that contains a scan +directive, the package the ZCML file points to is scanned. + +.. topic:: Declaratively Starting a Scan + + .. code-block:: python + :linenos: + + # helloworld.py + + from paste.httpserver import serve + from pyramid.response import Response + from pyramid.view import view_config + + @view_config() + def hello(request): + return Response('Hello') + + if __name__ == '__main__': + from pyramid.configuration 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') + + .. code-block:: xml + :linenos: + + <configure xmlns="http://namespaces.repoze.org"> + + <!-- configure.zcml --> + + <include package="pyramid.includes"/> + <scan package="."/> + + </configure> + +Which Mode Should I Use? +------------------------ + +A combination of imperative configuration, declarative configuration +via ZCML and scanning can be used to configure any application. They +are not mutually exclusive. + +The :mod:`pyramid` authors often recommend using mostly declarative +configuration, because it's the more traditional form of configuration +used in :mod:`pyramid` applications, it can be overridden and +extended by third party deployers, and there are more examples for it +"in the wild". + +However, imperative mode configuration can be simpler to understand, +and the framework is not "opinionated" about the choice. This book +presents examples in both styles, mostly interchangeably. You can +choose the mode that best fits your brain as necessary. + +.. index:: + single: ZCML directive; route + +.. _zcml_route_configuration: + +Configuring a Route via ZCML +---------------------------- + +Instead of using the imperative +:meth:`pyramid.configuration.Configurator.add_route` method to add a new +route, you can alternately use :term:`ZCML`. :ref:`route_directive` +statements in a :term:`ZCML` file used by your application is a sign that +you're using :term:`URL dispatch`. For example, the following :term:`ZCML +declaration` causes a route to be added to the application. + +.. code-block:: xml + :linenos: + + <route + name="myroute" + pattern="/prefix/:one/:two" + view=".views.myview" + /> + +.. note:: + + Values prefixed with a period (``.``) within the values of ZCML + attributes such as the ``view`` attribute of a ``route`` mean + "relative to the Python package directory in which this + :term:`ZCML` file is stored". So if the above ``route`` + declaration was made inside a ``configure.zcml`` file that lived in + the ``hello`` package, you could replace the relative + ``.views.myview`` with the absolute ``hello.views.myview`` Either + the relative or absolute form is functionally equivalent. It's + often useful to use the relative form, in case your package's name + changes. It's also shorter to type. + +The order that routes are evaluated when declarative configuration is used +is the order that they appear relative to each other in the ZCML file. + +See :ref:`route_directive` for full ``route`` ZCML directive +documentation. + +.. index:: + triple: view; zcml; static resource + +.. _zcml_static_resources_section: + +Serving Static Resources Using ZCML +------------------------------------ + +Use of the ``static`` ZCML directive makes static files available at a name +relative to the application root URL, e.g. ``/static``. + +Note that the ``path`` provided to the ``static`` ZCML directive may be a +fully qualified :term:`resource specification`, a package-relative path, or +an *absolute path*. The ``path`` with the value ``a/b/c/static`` of a +``static`` directive in a ZCML file that resides in the "mypackage" package +will resolve to a package-qualified resource such as +``some_package:a/b/c/static``. + +Here's an example of a ``static`` ZCML directive that will serve files +up under the ``/static`` URL from the ``/var/www/static`` directory of +the computer which runs the :mod:`pyramid` application using an +absolute path. + +.. code-block:: xml + :linenos: + + <static + name="static" + path="/var/www/static" + /> + +Here's an example of a ``static`` directive 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:`resource specification`. + +.. code-block:: xml + :linenos: + + <static + name="static" + path="some_package:a/b/c/static" + /> + +Here's an example of a ``static`` directive that will serve files up +under the ``/static`` URL from the ``static`` directory of the Python +package in which the ``configure.zcml`` file lives using a +package-relative path. + +.. code-block:: xml + :linenos: + + <static + name="static" + path="static" + /> + +Whether you use for ``path`` a fully qualified resource specification, +an absolute path, or a package-relative 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 ``static`` ZCML directive 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 resources +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, the ``static`` ZCML directive may be fed a ``name`` +argument which is ``http://example.com/images``: + +.. code-block:: xml + :linenos: + + <static + name="http://example.com/images" + path="mypackage:images" + /> + +Because the ``static`` ZCML directive 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.url.static_url` will generate a URL +something like ``http://example.com/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 :meth:`pyramid.configuration.Configurator.add_static_view` method offers +an imperative equivalent to the ``static`` ZCML directive. Use of the +``add_static_view`` imperative configuration method is completely equivalent +to using ZCML for the same purpose. See :ref:`static_resources_section` for +more information. + +.. _zcml_authorization_policy: + +Enabling an Authorization Policy Via ZCML +----------------------------------------- + +If you'd rather use :term:`ZCML` to specify an authorization policy +than imperative configuration, modify the ZCML file loaded by your +application (usually named ``configure.zcml``) to enable an +authorization policy. + +For example, to enable a policy which compares the value of an "auth +ticket" cookie passed in the request's environment which contains a +reference to a single :term:`principal` against the principals present +in any :term:`ACL` found in model data when attempting to call some +:term:`view`, modify your ``configure.zcml`` to look something like +this: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://pylonshq.com/pyramid"> + + <!-- views and other directives before this... --> + + <authtktauthenticationpolicy + secret="iamsosecret"/> + + <aclauthorizationpolicy/> + + </configure> + +"Under the hood", these statements cause an instance of the class +:class:`pyramid.authentication.AuthTktAuthenticationPolicy` to be +injected as the :term:`authentication policy` used by this application +and an instance of the class +:class:`pyramid.authorization.ACLAuthorizationPolicy` to be +injected as the :term:`authorization policy` used by this application. + +:mod:`pyramid` ships with a number of authorization and +authentication policy ZCML directives that should prove useful. See +:ref:`authentication_policies_directives_section` and +:ref:`authorization_policies_directives_section` for more information. + +.. index:: + pair: ZCML directive; authentication policy + +.. _authentication_policies_directives_section: + +Built-In Authentication Policy ZCML Directives +---------------------------------------------- + +Instead of configuring an authentication policy and authorization +policy imperatively, :mod:`pyramid` ships with a few "pre-chewed" +authentication policy ZCML directives that you can make use of within +your application. + +``authtktauthenticationpolicy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When this directive is used, authentication information is obtained +from an "auth ticket" cookie value, assumed to be set by a custom +login form. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <authtktauthenticationpolicy + secret="goshiamsosecret" + callback=".somemodule.somefunc" + cookie_name="mycookiename" + secure="false" + include_ip="false" + timeout="86400" + reissue_time="600" + max_age="31536000" + path="/" + http_only="false" + /> + +See :ref:`authtktauthenticationpolicy_directive` for details about +this directive. + +``remoteuserauthenticationpolicy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When this directive is used, authentication information is obtained +from a ``REMOTE_USER`` key in the WSGI environment, assumed to +be set by a WSGI server or an upstream middleware component. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <remoteuserauthenticationpolicy + environ_key="REMOTE_USER" + callback=".somemodule.somefunc" + /> + +See :ref:`remoteuserauthenticationpolicy_directive` for detailed +information. + +``repozewho1authenticationpolicy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When this directive is used, authentication information is obtained +from a ``repoze.who.identity`` key in the WSGI environment, assumed to +be set by :term:`repoze.who` middleware. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <repozewho1authenticationpolicy + identifier_name="auth_tkt" + callback=".somemodule.somefunc" + /> + +See :ref:`repozewho1authenticationpolicy_directive` for detailed +information. + +.. index:: + pair: ZCML directive; authorization policy + +.. _authorization_policies_directives_section: + +Built-In Authorization Policy ZCML Directives +--------------------------------------------- + +``aclauthorizationpolicy`` + +When this directive is used, authorization information is obtained +from :term:`ACL` objects attached to model instances. + +An example of its usage, with all attributes fully expanded: + +.. code-block:: xml + :linenos: + + <aclauthorizationpolicy/> + +In other words, it has no configuration attributes; its existence in a +``configure.zcml`` file enables it. + +See :ref:`aclauthorizationpolicy_directive` for detailed information. + +.. Todo +.. ---- + +.. - ``narr/project.rst`` chapter describes execution of a paster template that +.. is based on XML. + +.. - Skipped views chapter. + +.. - i18n chapter still has topics for ZCML + +.. - events chapter still has topics for ZCML + +.. - hooks chapter still has topics for ZCML + +.. - resources chapter still has topics for ZCML diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index 88b42eabf..ac482895b 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -26,11 +26,11 @@ application through methods of the :term:`Configurator` (except for the :meth:`pyramid.configuration.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. +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 +:ref:`declarative_chapter` for information about using ZCML. Fundamental Plugpoints ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst index 67dd6b8fb..9be087036 100644 --- a/docs/narr/firstapp.rst +++ b/docs/narr/firstapp.rst @@ -17,8 +17,8 @@ explain in more detail how the application works. .. _helloworld_imperative: -Hello World, Goodbye World (Imperative) ---------------------------------------- +Hello World, Goodbye World +-------------------------- Here's one of the very simplest :mod:`pyramid` applications, configured imperatively: @@ -369,295 +369,14 @@ Our hello world application is one of the simplest possible that it's configured imperatively because the full power of Python is available to us as we perform configuration tasks. -.. index:: - single: helloworld (declarative) - -.. _helloworld_declarative: - -Hello World, Goodbye World (Declarative) ----------------------------------------- - -Another almost entirely equivalent mode of application configuration -exists named *declarative* configuration. :mod:`pyramid` can be -configured for the same "hello world" application "declaratively", if -so desired. - -To do so, first, create a file named ``helloworld.py``: - -.. code-block:: python - :linenos: - - from pyramid.configuration import Configurator - from pyramid.response import Response - from paste.httpserver import serve - - def hello_world(request): - return Response('Hello world!') - - def goodbye_world(request): - return Response('Goodbye world!') - - 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') - -Then create a file named ``configure.zcml`` in the same directory as -the previously created ``helloworld.py``: - -.. code-block:: xml - :linenos: - - <configure xmlns="http://pylonshq.com/pyramid"> - - <include package="pyramid.includes" /> - - <view - view="helloworld.hello_world" - /> - - <view - name="goodbye" - view="helloworld.goodbye_world" - /> - - </configure> - -This pair of files forms an application functionally equivalent to the -application we created earlier in :ref:`helloworld_imperative`. We can run -it the same way. - -.. code-block:: bash - - $ python helloworld.py - serving on 0.0.0.0:8080 view at http://127.0.0.1:8080 - -Let's examine the differences between the code in that section and the code -above. In :ref:`helloworld_imperative_appconfig`, we had the following lines -within the ``if __name__ == '__main__'`` section of ``helloworld.py``: - -.. code-block:: python - :linenos: - - 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') - -In our "declarative" code, we've added a call to the -:meth:`pyramid.configuration.Configurator.load_zcml` method with -the value ``configure.zcml``, and we've removed the lines which read -``config.add_view(hello_world)`` and ``config.add_view(goodbye_world, -name='goodbye')``, so that it now reads as: - -.. code-block:: python - :linenos: - - 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') - -Everything else is much the same. - -The ``config.load_zcml('configure.zcml')`` line tells the configurator -to load configuration declarations from the ``configure.zcml`` file -which sits next to ``helloworld.py``. Let's take a look at the -``configure.zcml`` file now: - -.. code-block:: xml - :linenos: - - <configure xmlns="http://pylonshq.com/pyramid"> - - <include package="pyramid.includes" /> - - <view - view="helloworld.hello_world" - /> - - <view - name="goodbye" - view="helloworld.goodbye_world" - /> - - </configure> - -We already understand what the view code does, because the application -is functionally equivalent to the application described in -:ref:`helloworld_imperative`, but use of :term:`ZCML` is new. Let's -break that down tag-by-tag. - -The ``<configure>`` Tag -~~~~~~~~~~~~~~~~~~~~~~~ - -The ``configure.zcml`` ZCML file contains this bit of XML: - -.. code-block:: xml - :linenos: - - <configure xmlns="http://pylonshq.com/pyramid"> - - <!-- other directives --> - - </configure> - -Because :term:`ZCML` is XML, and because XML requires a single root -tag for each document, every ZCML file used by :mod:`pyramid` must -contain a ``configure`` container directive, which acts as the root -XML tag. It is a "container" directive because its only job is to -contain other directives. - -See also :ref:`configure_directive` and :ref:`word_on_xml_namespaces`. - -The ``<include>`` Tag -~~~~~~~~~~~~~~~~~~~~~ - -The ``configure.zcml`` ZCML file contains this bit of XML within the -``<configure>`` root tag: - -.. code-block:: xml - - <include package="pyramid.includes" /> - -This self-closing tag instructs :mod:`pyramid` to load a ZCML file -from the Python package with the :term:`dotted Python name` -``pyramid.includes``, as specified by its ``package`` attribute. -This particular ``<include>`` declaration is required because it -actually allows subsequent declaration tags (such as ``<view>``, which -we'll see shortly) to be recognized. The ``<include>`` tag -effectively just includes another ZCML file, causing its declarations -to be executed. In this case, we want to load the declarations from -the file named ``configure.zcml`` within the -:mod:`pyramid.includes` Python package. We know we want to load -the ``configure.zcml`` from this package because ``configure.zcml`` is -the default value for another attribute of the ``<include>`` tag named -``file``. We could have spelled the include tag more verbosely, but -equivalently as: - -.. code-block:: xml - :linenos: - - <include package="pyramid.includes" - file="configure.zcml"/> - -The ``<include>`` tag that includes the ZCML statements implied by the -``configure.zcml`` file from the Python package named -:mod:`pyramid.includes` is basically required to come before any -other named declaration in an application's ``configure.zcml``. If it -is not included, subsequent declaration tags will fail to be -recognized, and the configuration system will generate an error at -startup. However, the ``<include package="pyramid.includes"/>`` -tag needs to exist only in a "top-level" ZCML file, it needn't also -exist in ZCML files *included by* a top-level ZCML file. - -See also :ref:`include_directive`. - -The ``<view>`` Tag -~~~~~~~~~~~~~~~~~~ - -The ``configure.zcml`` ZCML file contains these bits of XML *after* the -``<include>`` tag, but *within* the ``<configure>`` root tag: - -.. code-block:: xml - :linenos: - - <view - view="helloworld.hello_world" - /> - - <view - name="goodbye" - view="helloworld.goodbye_world" - /> - -These ``<view>`` declaration tags direct :mod:`pyramid` to create -two :term:`view configuration` registrations. The first ``<view>`` -tag has an attribute (the attribute is also named ``view``), which -points at a :term:`dotted Python name`, referencing the -``hello_world`` function defined within the ``helloworld`` package. -The second ``<view>`` tag has a ``view`` attribute which points at a -:term:`dotted Python name`, referencing the ``goodbye_world`` function -defined within the ``helloworld`` package. The second ``<view>`` tag -also has an attribute called ``name`` with a value of ``goodbye``. - -These effect of the ``<view>`` tag declarations we've put into our -``configure.zcml`` is functionally equivalent to the effect of lines -we've already seen in an imperatively-configured application. We're -just spelling things differently, using XML instead of Python. - -In our previously defined application, in which we added view -configurations imperatively, we saw this code: - -.. ignore-next-block -.. code-block:: python - :linenos: - - config.add_view(hello_world) - config.add_view(goodbye_world, name='goodbye') - -Each ``<view>`` declaration tag encountered in a ZCML file effectively -invokes the :meth:`pyramid.configuration.Configurator.add_view` -method on the behalf of the developer. Various attributes can be -specified on the ``<view>`` tag which influence the :term:`view -configuration` it creates. - -Since the relative ordering of calls to -:meth:`pyramid.configuration.Configurator.add_view` doesn't matter -(see the sidebar entitled *View Dispatch and Ordering* within -:ref:`adding_configuration`), the relative order of ``<view>`` tags in -ZCML doesn't matter either. The following ZCML orderings are -completely equivalent: - -.. topic:: Hello Before Goodbye - - .. code-block:: xml - :linenos: - - <view - view="helloworld.hello_world" - /> - - <view - name="goodbye" - view="helloworld.goodbye_world" - /> - -.. topic:: Goodbye Before Hello - - .. code-block:: xml - :linenos: - - <view - name="goodbye" - view="helloworld.goodbye_world" - /> - - <view - view="helloworld.hello_world" - /> - -We've now configured a :mod:`pyramid` helloworld application -declaratively. More information about this mode of configuration is -available in :ref:`declarative_configuration` and within -:ref:`zcml_reference`. +An example of using *declarative* configuration to perform the same task is +available within :ref:`declarative_configuration`. References ---------- For more information about the API of a :term:`Configurator` object, -see :class:`pyramid.configuration.Configurator` . The equivalent -ZCML declaration tags are introduced in :ref:`zcml_reference`. +see :class:`pyramid.configuration.Configurator` . For more information about :term:`view configuration`, see :ref:`views_chapter`. diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index cdac3642a..24ae63059 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -33,33 +33,24 @@ in isolation before trying to combine them. URL Dispatch Only ~~~~~~~~~~~~~~~~~ -An application that uses :term:`url dispatch` exclusively to map URLs -to code will often have declarations like this within :term:`ZCML`: +An application that uses :term:`url dispatch` exclusively to map URLs to code +will often have statements like this within your application startup +configuration: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar" - name="foobar" - view=".views.foobar" - /> + # config is an instance of pyramid.configuration.Configurator - <route - pattern=":baz/:buz" - name="bazbuz" - view=".views.bazbuz" - /> + config.add_route('foobar', ':foo/:bar', view='myproject.views.foobar') + config.add_route('bazbuz', ':baz/:buz', view='myproject.views.bazbuz') Each :term:`route` typically corresponds to a single view callable, and when that route is matched during a request, the view callable named by the ``view`` attribute is invoked. -Typically, an application that uses only URL dispatch won't perform -any configuration in ZCML that includes a ``<view>`` declaration and -won't have any calls to -:meth:`pyramid.configuration.Configurator.add_view` in its startup -code. +Typically, an application that uses only URL dispatch won't perform any calls +to :meth:`pyramid.configuration.Configurator.add_view` in its startup code. Traversal Only ~~~~~~~~~~~~~~ @@ -67,28 +58,22 @@ Traversal Only An application that uses only traversal will have view configuration declarations that look like this: -.. code-block:: xml +.. code-block:: python :linenos: - <view - name="foobar" - view=".views.foobar" - /> + # config is an instance of pyramid.configuration.Configurator - <view - name="bazbuz" - view=".views.bazbuz" - /> + config.add_view('mypackage.views.foobar', name='foobar') + config.add_view('mypackage.views.bazbuz', name='bazbuz') When the above configuration is applied to an application, the -``.views.foobar`` view callable above will be called when the URL -``/foobar`` is visited. Likewise, the view ``.views.bazbuz`` will be -called when the URL ``/bazbuz`` is visited. +``mypackage.views.foobar`` view callable above will be called when the URL +``/foobar`` is visited. Likewise, the view ``mypackage.views.bazbuz`` will +be called when the URL ``/bazbuz`` is visited. -An application that uses :term:`traversal` exclusively to map URLs to -code usually won't have any ZCML ``<route>`` declarations nor will it -make any calls to the -:meth:`pyramid.configuration.Configurator.add_route` method. +Typically, an application that uses traversal exclusively won't perform any +calls to :meth:`pyramid.configuration.Configurator.add_route` in its startup +code. Hybrid Applications ------------------- @@ -197,13 +182,10 @@ A hybrid application most often implies the inclusion of a route configuration that contains the special token ``*traverse`` at the end of a route's pattern: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar/*traverse" - name="home" - /> + config.add_route('home', ':foo/:bar/*traverse') A ``*traverse`` token at the end of the pattern in a route's configuration implies a "remainder" *capture* value. When it is used, @@ -215,13 +197,12 @@ remainder becomes the path used to perform traversal. The ``*remainder`` route pattern syntax is explained in more detail within :ref:`route_pattern_syntax`. -Note that unlike the examples provided within -:ref:`urldispatch_chapter`, the ``<route>`` configuration named -previously does not name a ``view`` attribute. This is because a -hybrid mode application relies on :term:`traversal` to do -:term:`context finding` and :term:`view lookup` instead of invariably -invoking a specific view callable named directly within the matched -route's configuration. +Note that unlike the examples provided within :ref:`urldispatch_chapter`, the +``add_route`` configuration statement named previously does not pass a +``view`` argument. This is because a hybrid mode application relies on +:term:`traversal` to do :term:`context finding` and :term:`view lookup` +instead of invariably invoking a specific view callable named directly within +the matched route's configuration. Because the pattern of the above route ends with ``*traverse``, when this route configuration is matched during a request, :mod:`pyramid` @@ -259,14 +240,11 @@ Above, we've defined a (bogus) graph here that can be traversed, and a ``root_factory`` function that can be used as part of a particular route configuration statement: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar/*traverse" - name="home" - factory=".routes.root_factory" - /> + config.add_route('home', ':foo/:bar/*traverse', + factory='mypackage.routes.root_factory') The ``factory`` above points at the function we've defined. It will return an instance of the ``Traversable`` class as a root object @@ -315,32 +293,26 @@ but with a caveat: in order for view lookup to work, we need to define a view configuration that will match when :term:`view lookup` is invoked after a route matches: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar/*traverse" - name="home" - factory=".routes.root_factory" - /> - - <view - route_name="home" - view=".views.myview" - /> + config.add_route('home', ':foo/:bar/*traverse', + factory='mypackage.routes.root_factory') + config.add_view('mypackage.views.myview', route_name='home') -Note that the above ``view`` declaration includes a ``route_name`` -argument. Views that include a ``route_name`` argument are meant to -associate a particular view declaration with a route, using the -route's name, in order to indicate that the view should *only be -invoked when the route matches*. +Note that the above call to +:meth:`pyramid.configuration.Configurator.add_view` includes a ``route_name`` +argument. View configurations that include a ``route_name`` argument are +meant to associate a particular view declaration with a route, using the +route's name, in order to indicate that the view should *only be invoked when +the route matches*. -View configurations may have a ``route_name`` attribute which refers -to the value of the ``<route>`` declaration's ``name`` attribute. In -the above example, the route name is ``home``, referring to the name -of the route defined above it. +Calls to :meth:`pyramid.configuration.Configurator.add_view` may pass a +``route_name`` attribute which refers to the value of an existing route's +``name`` argument. In the above example, the route name is ``home``, +referring to the name of the route defined above it. -The above ``.views.myview`` view will be invoked when: +The above ``mypackage.views.myview`` view callable will be invoked when: - the route named "home" is matched @@ -351,29 +323,18 @@ The above ``.views.myview`` view will be invoked when: It is also possible to declare alternate views that may be invoked when a hybrid route is matched: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar/*traverse" - name="home" - factory=".routes.root_factory" - /> + config.add_route('home', ':foo/:bar/*traverse', + factory='mypackage.routes.root_factory') + config.add_view('mypackage.views.myview', name='home') + config.add_view('mypackage.views.another_view', name='another', + route_name='home') - <view - route_name="home" - view=".views.myview" - /> - - <view - route_name="home" - name="another" - view=".views.another_view" - /> - -The ``view`` declaration for ``.views.another_view`` above names a -different view and, more importantly, a different :term:`view name`. -The above ``.views.another_view`` view will be invoked when: +The ``add_view`` call for ``mypackage.views.another_view`` above names a +different view and, more importantly, a different :term:`view name`. The +above ``mypackage.views.another_view`` view will be invoked when: - the route named "home" is matched @@ -381,10 +342,10 @@ The above ``.views.another_view`` view will be invoked when: - the :term:`context` is any object. -For instance, if the URL ``http://example.com/one/two/a/another`` is -provided to an application that uses the previously mentioned object -graph, the ``.views.another`` view callable will be called instead of -the ``.views.myview`` view callable because the :term:`view name` will +For instance, if the URL ``http://example.com/one/two/a/another`` is provided +to an application that uses the previously mentioned object graph, the +``mypackage.views.another`` view callable will be called instead of the +``mypackage.views.myview`` view callable because the :term:`view name` will be ``another`` instead of the empty string. More complicated matching can be composed. All arguments to *route* @@ -397,26 +358,21 @@ Using the ``traverse`` Argument In a Route Definition Rather than using the ``*traverse`` remainder marker in a pattern, you can use the ``traverse`` argument to the -:meth:`pyramid.configuration.Configurator.add_route`` method or the -``traverse`` attribute of the :ref:`route_directive` ZCML directive. -(either method is equivalent). +:meth:`pyramid.configuration.Configurator.add_route`` method. When you use the ``*traverse`` remainder marker, the traversal path is limited to being the remainder segments of a request URL when a route matches. However, when you use the ``traverse`` argument or attribute, you have more control over how to compose a traversal path. -Here's a use of the ``traverse`` pattern in a ZCML ``route`` -declaration: +Here's a use of the ``traverse`` pattern in a call to +:meth:`pyramid.configuration.Configurator.add_route`: -.. code-block:: xml +.. code-block:: python :linenos: - <route - name="abc" - pattern="/articles/:article/edit" - traverse="/articles/:article" - /> + config.add_route('abc', '/articles/:article/edit', + traverse='/articles/:article') The syntax of the ``traverse`` argument is the same as it is for ``pattern``. @@ -446,28 +402,20 @@ with this route). Making Global Views Match +++++++++++++++++++++++++ -By default, view configurations that don't mention a ``route_name`` -will be not found by view lookup when a route that mentions a -``*traverse`` in its pattern matches. You can make these match forcibly -by adding the ``use_global_views`` flag to the route definition. For -example, the ``views.bazbuz`` view below will be found if the route -named ``abc`` below is matched and the ``PATH_INFO`` is -``/abc/bazbuz``, even though the view configuration statement does not -have the ``route_name="abc"`` attribute. +By default, view configurations that don't mention a ``route_name`` will be +not found by view lookup when a route that mentions a ``*traverse`` in its +pattern matches. You can make these match forcibly by adding the +``use_global_views`` flag to the route definition. For example, the +``myproject.views.bazbuz`` view below will be found if the route named +``abc`` below is matched and the ``PATH_INFO`` is ``/abc/bazbuz``, even +though the view configuration statement does not have the +``route_name="abc"`` attribute. -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern="/abc/*traverse" - name="abc" - use_global_views="True" - /> - - <view - name="bazbuz" - view=".views.bazbuz" - /> + config.add_route('abc', '/abc/*traverse', use_global_views=True) + config.add_view('myproject.views.bazbuz', name='bazbuz') .. index:: single: route subpath @@ -491,18 +439,15 @@ but the traversal algorithm will return a :term:`subpath` list implied by the capture value of ``*subpath``. You'll see this pattern most commonly in route declarations that look like this: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern="/static/*subpath" - name="static" - view=".views.static_view" - /> + config.add_route('static', '/static/*subpath', + view='mypackage.views.static_view') -Where ``.views.static_view`` is an instance of -:class:`pyramid.view.static`. This effectively tells the static -helper to traverse everything in the subpath as a filename. +Where ``mypackage.views.static_view`` is an instance of +:class:`pyramid.view.static`. This effectively tells the static helper to +traverse everything in the subpath as a filename. Corner Cases ------------ @@ -515,52 +460,35 @@ Registering a Default View for a Route That Has a ``view`` Attribute It is an error to provide *both* a ``view`` argument to a :term:`route configuration` *and* a :term:`view configuration` which names a -``route_name`` that has no ``name`` value or the empty ``name`` value. -For example, this pair of route/view ZCML declarations will generate a -"conflict" error at startup time. +``route_name`` that has no ``name`` value or the empty ``name`` value. For +example, this pair of declarations will generate a "conflict" error at +startup time. -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar/*traverse" - name="home" - view=".views.home" - /> - - <view - route_name="home" - view=".views.another" - /> + config.add_route('home', ':foo/:bar/*traverse', + view='myproject.views.home') + config.add_view('myproject.views.another', route_name='home') -This is because the ``view`` attribute of the ``<route>`` statement -above is an *implicit* default view when that route matches. -``<route>`` declarations don't *need* to supply a view attribute. For -example, this ``<route>`` statement: +This is because the ``view`` argument to the +:meth:`pyramid.configuration.Configurator.add_route` above is an *implicit* +default view when that route matches. ``add_route`` calls don't *need* to +supply a view attribute. For example, this ``add_route`` call: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar/*traverse" - name="home" - view=".views.home" - /> + config.add_route('home', ':foo/:bar/*traverse', + view='myproject.views.home') Can also be spelled like so: -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern=":foo/:bar/*traverse" - name="home" - /> - - <view - route_name="home" - view=".views.home" - /> + config.add_route('home', ':foo/:bar/*traverse') + config.add_view('myproject.views.home', route_name='home') The two spellings are logically equivalent. In fact, the former is just a syntactical shortcut for the latter. @@ -570,42 +498,26 @@ Binding Extra Views Against a Route Configuration that Doesn't Have a ``*travers Here's another corner case that just makes no sense. -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern="/abc" - name="abc" - view=".views.abc" - /> + config.add_route('abc', '/abc', view='myproject.views.abc') + config.add_view('myproject.views.bazbuz', name='bazbuz', + route_name='abc') - <view - name="bazbuz" - view=".views.bazbuz" - route_name="abc" - /> +The above view declaration is useless, because it will never be matched when +the route it references has matched. Only the view associated with the route +itself (``myproject.views.abc``) will ever be invoked when the route matches, +because the default view is always invoked when a route matches and when no +post-match traversal is performed. -The above ``<view>`` declaration is useless, because it will never be -matched when the route it references has matched. Only the view -associated with the route itself (``.views.abc``) will ever be invoked -when the route matches, because the default view is always invoked -when a route matches and when no post-match traversal is performed. +To make the above view declaration non-useless, the special ``*traverse`` +token must end the route's pattern. For example: -To make the above ``<view>`` declaration non-useless, the special -``*traverse`` token must end the route's pattern. For example: - -.. code-block:: xml +.. code-block:: python :linenos: - <route - pattern="/abc/*traverse" - name="abc" - view=".views.abc" - /> - - <view - name="bazbuz" - view=".views.bazbuz" - route_name="abc" - /> + config.add_route('abc', '/abc/*traverse', view='myproject.views.abc') + config.add_view('myproject.views.bazbuz', name='bazbuz', + route_name='abc') diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 506d1a623..471ae8754 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -67,14 +67,19 @@ The included templates are these: URL mapping via :term:`URL dispatch` and Pylons-style view handlers, some extra functionality, and SQLAlchemy set up. -Each of these project templates uses :term:`ZCML` instead of -:term:`imperative configuration`. Each also makes the assumption that -you want your code to live in a Python :term:`package`. Even if your -application is extremely simple, it is useful to place code that -drives the application within a package, because a package is more -easily extended with new code. An application that lives inside a -package can also be distributed more easily than one which does not -live within a package. +The project templates which start with ``pyramid_`` uses :term:`ZCML` (see +:ref:`declarative_chapter`) instead of :term:`imperative configuration`. + +The project templates which start with ``pylons_`` use imperative +configuration (the type of configuration which we've been showing so far in +the book). + +Each also makes the assumption that you want your code to live in a Python +:term:`package`. Even if your application is extremely simple, it is useful +to place code that drives the application within a package, because a package +is more easily extended with new code. An application that lives inside a +package can also be distributed more easily than one which does not live +within a package. .. index:: single: creating a project diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 63d1cad25..f9fee59dd 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -59,9 +59,6 @@ views are accessible by completely anonymous users. In order to begin protecting views from execution based on security settings, you need to enable an authorization policy. -You can enable an authorization policy imperatively, or declaratively -via ZCML. - Enabling an Authorization Policy Imperatively ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -110,46 +107,8 @@ See also the :mod:`pyramid.authorization` and :mod:`pyramid.authentication` modules for alternate implementations of authorization and authentication policies. -Enabling an Authorization Policy Via ZCML -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you'd rather use :term:`ZCML` to specify an authorization policy -than imperative configuration, modify the ZCML file loaded by your -application (usually named ``configure.zcml``) to enable an -authorization policy. - -For example, to enable a policy which compares the value of an "auth -ticket" cookie passed in the request's environment which contains a -reference to a single :term:`principal` against the principals present -in any :term:`ACL` found in model data when attempting to call some -:term:`view`, modify your ``configure.zcml`` to look something like -this: - -.. code-block:: xml - :linenos: - - <configure xmlns="http://pylonshq.com/pyramid"> - - <!-- views and other directives before this... --> - - <authtktauthenticationpolicy - secret="iamsosecret"/> - - <aclauthorizationpolicy/> - - </configure> - -"Under the hood", these statements cause an instance of the class -:class:`pyramid.authentication.AuthTktAuthenticationPolicy` to be -injected as the :term:`authentication policy` used by this application -and an instance of the class -:class:`pyramid.authorization.ACLAuthorizationPolicy` to be -injected as the :term:`authorization policy` used by this application. - -:mod:`pyramid` ships with a number of authorization and -authentication policy ZCML directives that should prove useful. See -:ref:`authentication_policies_directives_section` and -:ref:`authorization_policies_directives_section` for more information. +You can also enable a security policy declaratively via ZCML. See +:ref:`zcml_authorization_policy`. .. index:: single: permissions @@ -166,19 +125,19 @@ security settings in a :term:`context`, you must pass a usually just strings, and they have no required composition: you can name permissions whatever you like. -For example, the following declaration protects the view named -``add_entry.html`` when invoked against a ``Blog`` context with the -``add`` permission: +For example, the following view declaration protects the view named +``add_entry.html`` when invoked against a ``Blog`` context with the ``add`` +permission using the :meth:`pyramid.configuration.Configurator.add_view` API: -.. code-block:: xml +.. code-block:: python :linenos: - <view - context=".models.Blog" - view=".views.blog_entry_add_view" - name="add_entry.html" - permission="add" - /> + # config is an instance of pyramid.configuration.Configurator + + config.add_view('mypackage.views.blog_entry_add_view', + name='add_entry.html', + context='mypackage.models.Blog', + permission='add') The equivalent view registration including the ``add`` permission name may be performed via the ``@view_config`` decorator: @@ -195,22 +154,15 @@ may be performed via the ``@view_config`` decorator: """ Add blog entry code goes here """ pass -Or the same thing can be done using the -:meth:`pyramid.configuration.Configurator.add_view` method: +Or the same thing can be done using the ``permission`` attribute of the ZCML +:ref:`view_directive` directive. -.. ignore-next-block -.. code-block:: python - :linenos: - - config.add_view(blog_entry_add_view, - context=Blog, name='add_entry.html', permission='add') - -As a result of any of these various view configuration statements, if -an authorization policy is in place when the view callable is found -during normal application operations, the requesting user will need to -possess the ``add`` permission against the :term:`context` to be able -to invoke the ``blog_entry_add_view`` view. If he does not, the -:term:`Forbidden view` will be invoked. +As a result of any of these various view configuration statements, if an +authorization policy is in place when the view callable is found during +normal application operations, the requesting user will need to possess the +``add`` permission against the :term:`context` to be able to invoke the +``blog_entry_add_view`` view. If he does not, the :term:`Forbidden view` +will be invoked. .. _setting_a_default_permission: @@ -348,15 +300,14 @@ The first element of any ACE is either the ACE matches. The second element is a :term:`principal`. The third argument is a permission or sequence of permission names. -A principal is usually a user id, however it also may be a group id if -your authentication system provides group information and the -effective :term:`authentication policy` policy is written to respect -group information. For example, the -:class:`pyramid.authentication.RepozeWho1AuthenicationPolicy` -enabled by the ``repozewho1authenticationpolicy`` ZCML directive -respects group information if you configure it with a ``callback``. -See :ref:`authentication_policies_directives_section` for more -information about the ``callback`` attribute. +A principal is usually a user id, however it also may be a group id if your +authentication system provides group information and the effective +:term:`authentication policy` policy is written to respect group information. +For example, the +:class:`pyramid.authentication.RepozeWho1AuthenicationPolicy` respects group +information if you configure it with a ``callback``. See +:ref:`authentication_policies_directives_section` for more information about +the ``callback`` attribute. Each ACE in an ACL is processed by an authorization policy *in the order dictated by the ACL*. So if you have an ACL like this: @@ -585,112 +536,6 @@ via print statements when a call to :func:`pyramid.security.has_permission` fails is often useful. .. index:: - pair: ZCML directive; authentication policy - -.. _authentication_policies_directives_section: - -Built-In Authentication Policy ZCML Directives ----------------------------------------------- - -Instead of configuring an authentication policy and authorization -policy imperatively, :mod:`pyramid` ships with a few "pre-chewed" -authentication policy ZCML directives that you can make use of within -your application. - -``authtktauthenticationpolicy`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When this directive is used, authentication information is obtained -from an "auth ticket" cookie value, assumed to be set by a custom -login form. - -An example of its usage, with all attributes fully expanded: - -.. code-block:: xml - :linenos: - - <authtktauthenticationpolicy - secret="goshiamsosecret" - callback=".somemodule.somefunc" - cookie_name="mycookiename" - secure="false" - include_ip="false" - timeout="86400" - reissue_time="600" - max_age="31536000" - path="/" - http_only="false" - /> - -See :ref:`authtktauthenticationpolicy_directive` for details about -this directive. - -``remoteuserauthenticationpolicy`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When this directive is used, authentication information is obtained -from a ``REMOTE_USER`` key in the WSGI environment, assumed to -be set by a WSGI server or an upstream middleware component. - -An example of its usage, with all attributes fully expanded: - -.. code-block:: xml - :linenos: - - <remoteuserauthenticationpolicy - environ_key="REMOTE_USER" - callback=".somemodule.somefunc" - /> - -See :ref:`remoteuserauthenticationpolicy_directive` for detailed -information. - -``repozewho1authenticationpolicy`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When this directive is used, authentication information is obtained -from a ``repoze.who.identity`` key in the WSGI environment, assumed to -be set by :term:`repoze.who` middleware. - -An example of its usage, with all attributes fully expanded: - -.. code-block:: xml - :linenos: - - <repozewho1authenticationpolicy - identifier_name="auth_tkt" - callback=".somemodule.somefunc" - /> - -See :ref:`repozewho1authenticationpolicy_directive` for detailed -information. - -.. index:: - pair: ZCML directive; authorization policy - -.. _authorization_policies_directives_section: - -Built-In Authorization Policy ZCML Directives ---------------------------------------------- - -``aclauthorizationpolicy`` - -When this directive is used, authorization information is obtained -from :term:`ACL` objects attached to model instances. - -An example of its usage, with all attributes fully expanded: - -.. code-block:: xml - :linenos: - - <aclauthorizationpolicy/> - -In other words, it has no configuration attributes; its existence in a -``configure.zcml`` file enables it. - -See :ref:`aclauthorizationpolicy_directive` for detailed information. - -.. index:: single: authentication policy (creating) .. _creating_an_authentication_policy: diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst index ef118d857..c86a279ee 100644 --- a/docs/narr/startup.rst +++ b/docs/narr/startup.rst @@ -102,30 +102,23 @@ press ``return`` after running ``paster serve MyProject.ini``. request to retrieve the application root. It is not called during startup, only when a request is handled. - ``settings`` dictionary contains all the options in the + The ``settings`` dictionary contains all the options in the ``[app:main]`` section of our .ini file except the "use" option (which is internal to paste). In this case, ``**settings`` will be something like ``{'reload_templates':'true', 'debug_authorization':'false', 'debug_notfound':'false'}``. -#. The ``app`` function then calls the - :meth:`pyramid.configuration.Configurator.load_zcml` method, - passing in a ``zcml_file`` value. ``zcml_file`` is the value of - the ``configure_zcml`` setting or a default of ``configure.zcml``. - This filename is relative to the run.py file that the ``app`` - function lives in. The ``load_zcml`` function processes each - :term:`ZCML declaration` in the ZCML file implied by the - ``zcml_file`` argument. If ``load_zcml`` fails to parse the ZCML - file (or any file which is included by the ZCML file), a - ``XMLConfigurationError`` is raised and processing ends. If it - succeeds, an :term:`application registry` is populated using all - the :term:`ZCML declaration` statements present in the file. - -#. The :meth:`pyramid.configuration.Configurator.make_wsgi_app` - method is called. The result is a :term:`router` instance. The - router is associated with the :term:`application registry` implied - by the configurator previously populated by ZCML. The router is a - WSGI application. +#. The ``app`` function then calls various methods on the an instance of the + class :class:`pyramid.configuration.Configurator` method. The intent of + calling these methods is to populate an :term:`application registry`, + which represents the :mod:`pyramid` configuration related to the + application. + +#. The :meth:`pyramid.configuration.Configurator.make_wsgi_app` method is + called. The result is a :term:`router` instance. The router is + associated with the :term:`application registry` implied by the + configurator previously populated by other methods run against the + Configurator. The router is a WSGI application. #. A :class:`pyramid.interfaces.IApplicationCreated` event is emitted (see :ref:`events_chapter` for more information about diff --git a/docs/narr/static.rst b/docs/narr/static.rst index 02ee68eb6..a60b84445 100644 --- a/docs/narr/static.rst +++ b/docs/narr/static.rst @@ -6,88 +6,65 @@ resources from a directory on a filesystem. This chapter describes how to configure :mod:`pyramid` to do so. .. index:: - triple: view; zcml; static resource single: add_static_view .. _static_resources_section: -Serving Static Resources Using a ZCML Directive ------------------------------------------------ - -Use of the ``static`` ZCML directive or the -:meth:`pyramid.configuration.configurator.add_static_view` method -is the preferred way to instruct :mod:`pyramid` to serve static -resources 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 ``static`` may be a fully qualified -:term:`resource specification`, a package-relative path, or an -*absolute path*. The ``path`` with the value ``a/b/c/static`` of a -``static`` directive in a ZCML file that resides in the "mypackage" -package will resolve to a package-qualified resource such as -``some_package:a/b/c/static``. - -Here's an example of a ``static`` ZCML directive that will serve files -up under the ``/static`` URL from the ``/var/www/static`` directory of -the computer which runs the :mod:`pyramid` application using an -absolute path. - -.. code-block:: xml - :linenos: +Serving Static Resources +------------------------ + +Use the :meth:`pyramid.configuration.Configurator.add_static_view` to +instruct :mod:`pyramid` to serve static resources such as JavaScript and CSS +files. This mechanism makes static files available at a name relative to the +application root URL, e.g. ``/static``. - <static - name="static" - path="/var/www/static" - /> +Note that the ``path`` provided to +:meth:`pyramid.configuration.Configurator.add_static_view` may be a fully +qualified :term:`resource specification` or an *absolute path*. -Here's an example of a ``static`` directive 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:`resource specification`. +Here's an example of a use of +:meth:`pyramid.configuration.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 :mod:`pyramid` application using an absolute +path. -.. code-block:: xml +.. code-block:: python :linenos: - <static - name="static" - path="some_package:a/b/c/static" - /> + # config is an instance of pyramid.configuration.Configurator + config.add_static_view(name='static', path='/var/www/static') -Here's an example of a ``static`` directive that will serve files up -under the ``/static`` URL from the ``static`` directory of the Python -package in which the ``configure.zcml`` file lives using a -package-relative path. +Here's an example of +:meth:`pyramid.configuration.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:`resource +specification`. -.. code-block:: xml +.. code-block:: python :linenos: - <static - name="static" - path="static" - /> - -Whether you use for ``path`` a fully qualified resource specification, -an absolute path, or a package-relative 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 + # config is an instance of pyramid.configuration.Configurator + config.add_static_view(name='static', path='some_package:a/b/c/static') + +Whether you use for ``path`` a fully qualified resource 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 ``static`` ZCML directive 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 resources -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`. +``/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.configuration.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 resources 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:: @@ -109,32 +86,30 @@ is used as the URL prefix when generating a URL using for a pattern; any setting name other than ``media_location`` could be used. -For example, the ``static`` ZCML directive may be fed a ``name`` -argument which is ``http://example.com/images``: +For example, :meth:`pyramid.configuration.Configurator.add_static_view` may +be fed a ``name`` argument which is ``http://example.com/images``: -.. code-block:: xml +.. code-block:: python :linenos: - <static - name="http://example.com/images" - path="mypackage:images" - /> - -Because the ``static`` ZCML directive 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.configuration.Configurator.add_static_view` will -generate a URL something like ``http://example.com/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 :meth:`pyramid.configuration.Configurator.add_static_view` -method offers an imperative equivalent to the ``static`` ZCML -directive. Use of the ``add_static_view`` imperative configuration -method is completely equivalent to using ZCML for the same purpose. + # config is an instance of pyramid.configuration.Configurator + config.add_static_view(name='http://example.com/images', + path='mypackage:images') + +Because :meth:`pyramid.configuration.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.configuration.Configurator.add_static_view` +will generate a URL something like ``http://example.com/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.configuration.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 resource urls @@ -145,42 +120,30 @@ method is completely equivalent to using ZCML for the same purpose. Generating Static Resource URLs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :ref:`static_directive` ZCML directive or a call to the -``add_static_view`` method of a -:class:`pyramid.configuration.Configurator` is used to register a -static resource directory, a special helper API named -:func:`pyramid.static_url` can be used to generate the appropriate -URL for a package resource that lives in one of the directories named -by the static registration ``path`` attribute. +When a :meth:`pyramid.configuration.Configurator.add_static_view`` method is +used to register a static resource directory, a special helper API named +:func:`pyramid.static_url` can be used to generate the appropriate URL for a +package resource 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 -in ZCML like so: +For example, let's assume you create a set of static declarations like so: -.. code-block:: xml +.. code-block:: python :linenos: - <static - name="static1" - path="resources/1" - /> - - <static - name="static2" - path="resources/2" - /> + config.add_static_view(name='static1', path='mypackage:resources/1') + config.add_static_view(name='static2', path='mypackage:resources/2') -These declarations create URL-accessible directories which have URLs -which begin, respectively, with ``/static1`` and ``/static2``. The -resources in the ``resources/1`` directory are consulted when a user -visits a URL which begins with ``/static1``, and the resources in the -``resources/2`` directory are consulted when a user visits a URL which -begins with ``/static2``. +These declarations create URL-accessible directories which have URLs which +begin, respectively, with ``/static1`` and ``/static2``. The resources in +the ``resources/1`` directory of the ``mypackage`` package are consulted when +a user visits a URL which begins with ``/static1``, and the resources in the +``resources/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 resources "by hand" in such a configuration. Instead, use the :func:`pyramid.url.static_url` API -to generate them for you. For example, let's imagine that the -following code lives in a module that shares the same directory as the -above ZCML file: +to generate them for you. For example: .. code-block:: python :linenos: @@ -189,8 +152,8 @@ above ZCML file: from pyramid.chameleon_zpt import render_template_to_response def my_view(request): - css_url = static_url('resources/1/foo.css', request) - js_url = static_url('resources/2/foo.js', request) + css_url = static_url('mypackage:resources/1/foo.css', request) + js_url = static_url('mypackage:resources/2/foo.js', request) return render_template_to_response('templates/my_template.pt', css_url = css_url, js_url = js_url) @@ -200,29 +163,23 @@ If the request "application URL" of the running system is ``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 in ZCML, the generated -URLs will continue to resolve properly after the rename. - -URLs may also be generated by :func:`pyramid.url.static_url` to -static resources that live *outside* the :mod:`pyramid` -application. This will happen when the ``name`` argument provided to -the ``static`` ZCML directive or the -:meth:`pyramid.configuration.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 -given to either the ZCML directive or the configurator API may be +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 +resources that live *outside* the :mod:`pyramid` application. This will +happen when the :meth:`pyramid.configuration.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:: xml +.. code-block:: python :linenos: - <static - name="static1" - path="mypackage:images" - /> + config.add_static_view(name='static1', path='mypackage:images') Under such a configuration, the URL generated by ``static_url`` for resources which begin with ``mypackage:images`` will be prefixed with @@ -270,34 +227,27 @@ your application root as below. "here-relative", it is relative to the package of the module in which the static view is defined. -Subsequently, you may wire this view up to be accessible as -``/static`` using either the -:mod:`pyramid.configuration.Configurator.add_view` method or the -``<view>`` ZCML directive in your application's ``configure.zcml`` -against either the class or interface that represents your root -object. For example (ZCML): - -.. code-block:: xml +Subsequently, you may wire this view up to be accessible as ``/static`` using +the :mod:`pyramid.configuration.Configurator.add_view` method in your +application's startup code against either the class or interface that +represents your root object. + +.. code-block:: python :linenos: - <view - context=".models.Root" - view=".static.static_view" - name="static" - /> + config.add_view('mypackage.static.static_view', name='static', + context='mypackage.models.Root') -In this case, ``.models.Root`` refers to the class of which your +In this case, ``mypackage.models.Root`` refers to the class of which your :mod:`pyramid` application's root object is an instance. -You can also provide a ``context`` of ``*`` if you want the name -``static`` to be accessible as the static view against any model. -This will also allow ``/static/foo.js`` to work, but it will allow for -``/anything/static/foo.js`` too, as long as ``anything`` itself is -resolvable. +You can also omit the ``context`` argument if you want the name ``static`` to +be accessible as the static view against any model. This will also allow +``/static/foo.js`` to work, but it will allow for ``/anything/static/foo.js`` +too, as long as ``anything`` itself is resolvable. -Note that you cannot use the :func:`pyramid.static_url` API to -generate URLs against resources made accessible by registering a -custom static view. +Note that you cannot use the :func:`pyramid.static_url` API to generate URLs +against resources made accessible by registering a custom static view. .. warning:: diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index f5fa87fce..2ad086ac7 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -35,11 +35,15 @@ hierarchy, URL dispatch is easier to use than traversal, and is often a more natural fit for creating an application that manipulates "flat" data. -The presence of :ref:`route_directive` statements in a :term:`ZCML` -file used by your application or the presence of calls to the -:meth:`pyramid.configuration.Configurator.add_route` method in -imperative configuration within your application is a sign that you're -using :term:`URL dispatch`. +The presence of calls to the +:meth:`pyramid.configuration.Configurator.add_route` method in imperative +configuration within your application is a sign that you're using :term:`URL +dispatch`. + +..note:: + + Route configuration may also be added to the system via term:`ZCML` (see + :ref:`zcml_route_configuration`). High-Level Operational Overview ------------------------------- @@ -72,14 +76,11 @@ application to uniquely identify a particular route when generating a URL. It also optionally has a ``factory``, a set of :term:`route predicate` parameters, and a set of :term:`view` parameters. -A route configuration may be added to the system via :term:`imperative -configuration` or via :term:`ZCML`. Both are completely equivalent. - .. index:: single: add_route -Configuring a Route Imperatively via The ``add_route`` Configurator Method -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Configuring a Route via The ``add_route`` Configurator Method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :meth:`pyramid.configuration.Configurator.add_route` method adds a single :term:`route configuration` to the :term:`application @@ -95,42 +96,7 @@ registry`. Here's an example: config.add_route('myroute', '/prefix/:one/:two', view=myview) .. index:: - single: ZCML directive; route - -Configuring a Route via ZCML -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Instead of using the imperative -:meth:`pyramid.configuration.Configurator.add_route` method to add -a new route, you can alternately use :term:`ZCML`. For example, the -following :term:`ZCML declaration` causes a route to be added to the -application. - -.. code-block:: xml - :linenos: - - <route - name="myroute" - pattern="/prefix/:one/:two" - view=".views.myview" - /> - -.. note:: - - Values prefixed with a period (``.``) within the values of ZCML - attributes such as the ``view`` attribute of a ``route`` mean - "relative to the Python package directory in which this - :term:`ZCML` file is stored". So if the above ``route`` - declaration was made inside a ``configure.zcml`` file that lived in - the ``hello`` package, you could replace the relative - ``.views.myview`` with the absolute ``hello.views.myview`` Either - the relative or absolute form is functionally equivalent. It's - often useful to use the relative form, in case your package's name - changes. It's also shorter to type. - - -See :ref:`route_directive` for full ``route`` ZCML directive -documentation. + single: route configuration; view callable Route Configuration That Names a View Callable ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -144,14 +110,26 @@ information about how to create view callables, see Here's an example route configuration that references a view callable: -.. code-block:: xml +.. code-block:: python :linenos: - <route - name="myroute" - pattern="/prefix/:one/:two" - view="mypackage.views.myview" - /> + # "config" below is presumed to be an instance of the + # pyramid.configuration.Configurator class; "myview" is assumed + # to be a "view callable" function + from myproject.views import myview + config.add_route('myroute', '/prefix/:one/:two', view=myview) + +You can also pass a :term:`dotted Python name` as the ``view`` argument +rather than an actual callable: + +.. code-block:: python + :linenos: + + # "config" below is presumed to be an instance of the + # pyramid.configuration.Configurator class; "myview" is assumed + # to be a "view callable" function + from myproject.views import myview + config.add_route('myroute', '/prefix/:one/:two', 'myproject.views.myview') When a route configuration names a ``view`` attribute, the :term:`view callable` named as that ``view`` attribute will always be found and @@ -365,15 +343,12 @@ they are added to the application at startup time. This is unlike :term:`traversal`, which depends on emergent behavior which happens as a result of traversing a graph. -The order that routes are evaluated when they are defined via -:term:`ZCML` is the order in which they appear in the ZCML relative to -each other. For routes added via the -:mod:`pyramid.configuration.Configurator.add_route` method, the -order that routes are evaluated is the order in which they are added -to the configuration imperatively. +For routes added via the :mod:`pyramid.configuration.Configurator.add_route` +method, the order that routes are evaluated is the order in which they are +added to the configuration imperatively. -For example, route configuration statements with the following -patterns might be added in the following order: +For example, route configuration statements with the following patterns might +be added in the following order: .. code-block:: text @@ -401,14 +376,14 @@ the route is used to generate a :term:`root` object. This object will usually be used as the :term:`context` of the view callable ultimately found via :term:`view lookup`. -.. code-block:: xml +.. code-block:: python + :linenos: + + config.add_route('abc', '/abc', 'myproject.views.theview', + factory='myproject.models.root_factory') - <route - pattern="/abc" - name="abc" - view=".views.theview" - factory=".models.root_factory" - /> +The factory can either be a Python object or a :term:`dotted Python name` (a +string) which points to such a Python oject, as it is above. In this way, each route can use a different factory, making it possible to supply a different :term:`context` object to the view @@ -425,7 +400,7 @@ within :ref:`hybrid_chapter`. Route Configuration Arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Route configuration statements may specify a large number of +Route configuration ``add_route`` statements may specify a large number of arguments. Many of these arguments are :term:`route predicate` arguments. A @@ -647,11 +622,10 @@ represent neither predicates nor view configuration information. Custom Route Predicates ~~~~~~~~~~~~~~~~~~~~~~~ -Each of the predicate callables fed to the ``custom_predicates`` -argument of :meth:`pyramid.configuration.Configurator.add_route` or -the ``custom_predicates`` ZCML attribute must be a callable accepting -two arguments. The first argument passed to a custom predicate is a -dictionary conventionally named ``info``. The second argument is the +Each of the predicate callables fed to the ``custom_predicates`` argument of +:meth:`pyramid.configuration.Configurator.add_route` must be a callable +accepting two arguments. The first argument passed to a custom predicate is +a dictionary conventionally named ``info``. The second argument is the current :term:`request` object. The ``info`` dictionary has a number of contained values: ``match`` is @@ -825,10 +799,7 @@ Routing Examples Let's check out some examples of how route configuration statements might be commonly declared, and what will happen if they are matched -by the information present in a request. The examples that follow -assume that :term:`ZCML` will be used to perform route configuration, -although you can use :term:`imperative configuration` equivalently if -you like. +by the information present in a request. .. _urldispatch_example1: @@ -838,14 +809,10 @@ Example 1 The simplest route declaration which configures a route match to *directly* result in a particular view callable being invoked: -.. code-block:: xml +.. code-block:: python :linenos: - <route - name="idea" - pattern="site/:id" - view="mypackage.views.site_view" - /> + config.add_route('idea', 'site/:id', view='mypackage.views.site_view') When a route configuration with a ``view`` attribute is added to the system, and an incoming request matches the *pattern* of the route @@ -888,26 +855,12 @@ Example 2 Below is an example of a more complicated set of route statements you might add to your application: -.. code-block:: xml +.. code-block:: python :linenos: - <route - name="idea" - pattern="ideas/:idea" - view="mypackage.views.idea_view" - /> - - <route - name="user" - pattern="users/:user" - view="mypackage.views.user_view" - /> - - <route - name="tag" - pattern="tags/:tag" - view="mypackage.views.tag_view" - /> + config.add_route('idea', 'ideas/:idea', view='mypackage.views.idea_view') + config.add_route('user', 'users/:user', view='mypackage.views.user_view') + config.add_route('tag', 'tags/:tags', view='mypackage.views.tag_view') The above configuration will allow :mod:`pyramid` to service URLs in these forms: @@ -944,28 +897,24 @@ URL by the process will be passed to the view callable. Example 3 ~~~~~~~~~ -The context object passed in to a view found as the result of URL -dispatch will, by default, be an instance of the object returned by -the :term:`root factory` configured at startup time (the -``root_factory`` argument to the :term:`Configurator` used to -configure the application). +The context object passed in to a view found as the result of URL dispatch +will, by default, be an instance of the object returned by the :term:`root +factory` configured at startup time (the ``root_factory`` argument to the +:term:`Configurator` used to configure the application). -You can override this behavior by passing in a ``factory`` argument to -the ZCML directive for a particular route. The ``factory`` should be -a callable that accepts a :term:`request` and returns an instance of a -class that will be the context used by the view. +You can override this behavior by passing in a ``factory`` argument to the +:meth:`pyramid.configuration.Configurator.add_route` method for a particular +route. The ``factory`` should be a callable that accepts a :term:`request` +and returns an instance of a class that will be the context used by the view. An example of using a route with a factory: -.. code-block:: xml +.. code-block:: python :linenos: - <route - name="idea" - pattern="ideas/:idea" - view=".views.idea_view" - factory=".models.Idea" - /> + config.add_route('idea', 'ideas/:idea', + view='myproject.views.idea_view', + factory='myproject.models.Idea') The above route will manufacture an ``Idea`` model as a :term:`context`, assuming that ``mypackage.models.Idea`` resolves to a @@ -988,31 +937,20 @@ It is possible to create a route declaration without a ``view`` attribute, but associate the route with a :term:`view callable` using a ``view`` declaration. -.. code-block:: xml +.. code-block:: python :linenos: - <route - name="idea" - pattern="site/:id" - /> - - <view - view="mypackage.views.site_view" - route_name="idea" - /> + config.add_route('idea', 'site/:id') + config.add_view(route_name='idea', view='mypackage.views.site_view') This set of configuration parameters creates a configuration completely equivalent to this example provided in :ref:`urldispatch_example1`: -.. code-block:: xml +.. code-block:: python :linenos: - <route - name="idea" - pattern="site/:id" - view="mypackage.views.site_view" - /> + config.add_route('idea', 'site/:id', view='mypackage.views.site_view') In fact, the spelling which names a ``view`` attribute is just syntactic sugar for the more verbose spelling which contains separate @@ -1028,29 +966,21 @@ in :ref:`hybrid_chapter`. Matching the Root URL --------------------- -It's not entirely obvious how to use a route pattern to match the root -URL ("/"). To do so, give the empty string as a pattern in a ZCML -``route`` declaration: +It's not entirely obvious how to use a route pattern to match the root URL +("/"). To do so, give the empty string as a pattern in a call to +:meth:`pyramid.configuration.Configurator.add_route`: .. code-block:: xml :linenos: - <route - pattern="" - name="root" - view=".views.root_view" - /> + config.add_route('root', '', 'mypackage.views.root_view') Or provide the literal string ``/`` as the pattern: .. code-block:: xml :linenos: - <route - pattern="/" - name="root" - view=".views.root_view" - /> + config.add_route('root', '/', 'mypackage.views.root_view') .. index:: single: generating route URLs @@ -1059,10 +989,9 @@ Or provide the literal string ``/`` as the pattern: Generating Route URLs --------------------- -Use the :func:`pyramid.url.route_url` function to generate URLs -based on route patterns. For example, if you've configured a route in -ZCML with the ``name`` "foo" and the ``pattern`` ":a/:b/:c", you might -do this. +Use the :func:`pyramid.url.route_url` function to generate URLs based on +route patterns. For example, if you've configured a route with the ``name`` +"foo" and the ``pattern`` ":a/:b/:c", you might do this. .. ignore-next-block .. code-block:: python @@ -1101,15 +1030,8 @@ your route configuration looks like so: .. code-block:: xml :linenos: - <route - view=".views.no_slash" - pattern="no_slash" - /> - - <route - view=".views.has_slash" - pattern="has_slash/" - /> + config.add_route('noslash', 'no_slash', 'myproject.views.no_slash') + config.add_route('hasslash', 'has_slash/', 'myproject.views.has_slash') If a request enters the application with the ``PATH_INFO`` value of ``/no_slash``, the first route will match. If a request enters the @@ -1126,27 +1048,14 @@ redirect to ``/has_slash/`` will be returned to the user's browser. Note that this will *lose* ``POST`` data information (turning it into a GET), so you shouldn't rely on this to redirect POST requests. -To configure the slash-appending not found view in your application, -change the application's ``configure.zcml``, adding the following -stanza: +To configure the slash-appending not found view in your application, change +the application's startup configuration, adding the following stanza: .. code-block:: xml :linenos: - <view - context="pyramid.exceptions.NotFound" - view="pyramid.view.append_slash_notfound_view" - /> - -Or use the :meth:`pyramid.configuration.Configurator.add_view` -method if you don't use ZCML: - -.. code-block:: python - :linenos: - - from pyramid.exceptions import NotFound - from pyramid.view import append_slash_notfound_view - config.add_view(append_slash_notfound_view, context=NotFound) + config.add_view(context='pyramid.exceptions.NotFound', + view='pyramid.view.append_slash_notfound_view') See :ref:`view_module` and :ref:`changing_the_notfound_view` for more information about the slash-appending not found view and for a more @@ -1227,44 +1136,17 @@ following in the ``mypackage.run`` module: environ = event.request.environ environ['mypackage.sqlcleaner'] = Cleanup(DBSession.remove) -Then in the ``configure.zcml`` of your package, inject the following: - -.. code-block:: xml - - <subscriber for="pyramid.interfaces.INewRequest" - handler="mypackage.run.handle_teardown"/> - -Or, if you don't use ZCML, but you do use a :term:`scan` add a -subscriber decorator: +Then add an event subscriber in your startup configuration: .. code-block:: python + :linenos: - from pyramid.events import subscriber - from pyramid.interfaces import INewRequest - - @subscriber(INewRequest) - def handle_teardown(event): - environ = event.request.environ - environ['mypackage.sqlcleaner'] = Cleanup(DBSession.remove) - -Or finally, it can be done imperatively via the ``add_subscriber`` -method of a :term:`Configurator`. - -.. code-block:: python - - from pyramid.interfaces import INewRequest - from pyramid.configuration imoport Configurator - - def handle_teardown(event): - environ = event.request.environ - environ['mypackage.sqlcleaner'] = Cleanup(DBSession.remove) - - config = Configurator() - config.add_subscriber(handle_teardown, INewRequest) + config.add_subscriber('mypackage.run.handle_teardown', + 'pyramid.events.NewRequest') -Any of the above three ways to register a handle_teardown subscriber -will cause the DBSession to be removed whenever the WSGI environment -is destroyed (usually at the end of every request). +Registering a handle_teardown subscriber will cause the DBSession to be +removed whenever the WSGI environment is destroyed (usually at the end of +every request). .. note:: This is only an example. In particular, it is not necessary to cause ``DBSession.remove`` to be called as the result of an |
