diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-11-23 09:59:24 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-11-23 09:59:24 +0000 |
| commit | f6c1495fa1712cca94de8034afafc73a6790f591 (patch) | |
| tree | 31ecc863337cbd4bd4fb4ed1f54d0547c366c02a | |
| parent | 772ef05cbabd6bcd7b2298938a3e7240eee40a0c (diff) | |
| download | pyramid-f6c1495fa1712cca94de8034afafc73a6790f591.tar.gz pyramid-f6c1495fa1712cca94de8034afafc73a6790f591.tar.bz2 pyramid-f6c1495fa1712cca94de8034afafc73a6790f591.zip | |
Documentation improvements.
| -rw-r--r-- | docs/narr/configuration.rst | 253 | ||||
| -rw-r--r-- | repoze/bfg/configuration.py | 45 |
2 files changed, 290 insertions, 8 deletions
diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst index 6be8daa73..09e412c30 100644 --- a/docs/narr/configuration.rst +++ b/docs/narr/configuration.rst @@ -67,6 +67,8 @@ There are a number of different mechanisms you may use to configure and *declarative* configuration. We'll examine both modes in the sections which follow. +.. _helloworld_imperative: + Hello World, Configured Imperatively ------------------------------------ @@ -174,6 +176,8 @@ requesting user agent. The view callable defined by the script does nothing but return a response with the body ``Hello world!``. +.. _helloworld_imperative_appconfig: + Application Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -318,10 +322,10 @@ an XML dialect. Declarative configuration mode is the configuration mode in which developers cede the most amount of control to the framework itself. Because application developers cede a more control to the framework, -it is also the hardest mode of configuration to understand. However, -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 +it is also harder to understand than purely imperative configuration. +However, 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. .. note:: @@ -367,3 +371,244 @@ previously created ``helloworld.py``: </configure> +This pair of files forms an application functionally equivalent to the +application we created earlier. Let's examine the differences between +the code described in :ref:`helloworld_imperative`" 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.view(hello_world) + app = config.make_wsgi_app() + simple_server.make_server('', 8080, app).serve_forever() + +In our "declarative" code, we've added a ``zcml_file`` argument to the +``Configurator`` constructor's argument list with the value +``configure.zcml``, and we've removed the line which reads +``config.view(hello_world)``, so that it now reads as: + +.. code-block:: python + :linenos: + + if __name__ == '__main__': + config = Configurator(zcml_file='configure.zcml') + app = config.make_wsgi_app() + simple_server.make_server('', 8080, app).serve_forever() + +Everything else is much the same. + +The ``zcml_file`` argument to the ``Configurator`` constructor 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://namespaces.repoze.org/bfg"> + + <include package="repoze.bfg.includes" /> + + <view + view="helloworld.hello_world" + /> + + </configure> + +The ``<configure>`` Tag +~~~~~~~~~~~~~~~~~~~~~~~ + +The ``configure.zcml`` ZCML file contains this bit of XML: + +.. code-block:: xml + :linenos: + + <configure xmlns="http://namespaces.repoze.org/bfg"> + ... body ... + </configure> + +Because :term:`ZCML` is XML, and because XML requires a single root +tag for each document, every ZCML file used by :mod:`repoze.bfg` must +contain a ``<configure>`` container tag, which acts as the root XML +tag. Usually, the start tag of the ``<configure>`` container tag has +a default namespace associated with it. In the file above, the +``xmlns="http:/namepaces.repoze.org/bfg"`` attribute of the +``configure`` start tag names the default XML namespace, which is +``http://namespaces.repoze.org/bfg``. See +:ref:`word_on_xml_namespaces` for more information about XML +namespaces. + +The ``<include>`` Tag +~~~~~~~~~~~~~~~~~~~~~ + +The ``configure.zcml`` ZCML file contains this bit of XML within the +root tag: + +.. code-block:: xml + :linenos: + + <include package="repoze.bfg.includes" /> + +This singleton (self-closing) tag instructs ZCML to load a ZCML file +from the Python package with the :term:`dotted Python name` +``repoze.bfg.includes``, as specified by its ``package`` attribute. +This particular ``<include>`` declaration is required because it +actually allows subseqent declaration tags (such as ``<view>``, which +we'll see shortly) to be recognized. The ``<include>`` tag +effectively just includes another ZCML file; this causes its +declarations to be executed. In this case, we want to load the +declarations from the file named ``configure.zcml`` within the +``repoze.bfg.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="repoze.bfg.includes" + file="configure.zcml"/> + +The ``<include>`` tag that includes the ZCML statements implied by the +``configure.zcml`` file from the Python package named +``repoze.bfg.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 a traceback. However, the +``<include package="repoze.bfg.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. + +The ``<view>`` Tag +~~~~~~~~~~~~~~~~~~ + +The ``configure.zcml`` ZCML file contains this bit of XML after the +``<include>`` tag, but within the root tag: + +.. code-block:: xml + :linenos: + + <view + view="helloworld.hello_world" + /> + +This ``<view>`` declaration tag directs :mod:`repoze.bfg` to create a +:term:`view configuration`. This ``<view>`` tag has an attribute +(also named ``view``), which points at a :term:`dotted Python name`, +referencing the ``hello_world`` function defined within the +``helloworld`` package. This tag is functionally equivalent to a +line we saw previously in our imperatively-configured application: + +.. code-block:: python + :linenos: + + config.view(hello_world) + +The ``<view>`` declaration tag effectively invokes the ``view`` method +of the ``Configurator`` object on your behalf. Various attributes can +be specified on the ``<view>`` tag which influence the :term:`view +configuration` it creates. + +The ``<view>`` tag is an example of a :mod:`repoze.bfg` declaration +tag. Other such tags include ``<route>``, ``<scan>``, ``<notfound>``, +``<forbidden>``, and others. All of these tags are effectively +"macros" which call methods on the ``Configurator`` object on your +behalf. + +ZCML Conflict Detection +~~~~~~~~~~~~~~~~~~~~~~~ + +An 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://namespaces.repoze.org/bfg"> + + <include package="repoze.bfg.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, a ``ConfigurationError`` will be raised when you attempt +to start the application with information about which tags might have +conflicted. + +.. _word_on_xml_namespaces: + +A Word On XML Namespaces +~~~~~~~~~~~~~~~~~~~~~~~~ + +Using the ``http://namespaces.repoze.org/bfg`` namespace as the +default XML namespace isn't strictly necessary; you can use a +different default namespace as the default. However, if you do, the +declaration tags which are defined by :mod:`repoze.bfg` such as the +``<view>`` declaration tag will need to be defined in such a way that +the XML parser that :mod:`repoze.bfg` uses knows which namespace the +:mod:`repoze.bfg` tags are associated with. For example, the +following files are all completely equivalent: + +.. topic:: Use of A Non-Default XML Namespace + + .. code-block:: xml + :linenos: + + <configure xmlns="http://namespaces.zope.org/zope" + xmlns:bfg="http://namespaces.repoze.org/bfg"> + + <include package="repoze.bfg.includes" /> + + <bfg:view + view="helloworld.hello_world" + /> + + </configure> + +.. topic:: Use of A Per-Tag XML Namespace Without A Default XML Namespace + + .. code-block:: xml + :linenos: + + <configure> + + <include package="repoze.bfg.includes" /> + + <view xmlns="http://namespaces.repoze.org/bfg" + view="helloworld.hello_world" + /> + + </configure> + +For more information about XML namespaces, see `this older, but simple +XML.com article <http://www.xml.com/pub/a/1999/01/namespaces.html>`_. + +Conclusions +----------- + +:mod:`repoze.bfg` allows an application to perform configuration tasks +either imperatively or declaratively. You can choose the mode that +best fits your brain as necessary. + +For more information about the API of the ``Configurator`` object, see +:ref:`configuration_module`. The equivalent ZCML declaration tags are +introduced in narrative documentation chapters as necessary. + diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py index 2175fdf4e..970edc71c 100644 --- a/repoze/bfg/configuration.py +++ b/repoze/bfg/configuration.py @@ -74,8 +74,10 @@ DEFAULT_RENDERERS = ( class Configurator(object): """ A Configurator is used to configure a :mod:`repoze.bfg` - :term:`application registry`. The Configurator accepts two - arguments: ``registry`` and ``package``. + :term:`application registry`. The Configurator accepts a number + of arguments: ``registry``, ``package``, ``settings``, + ``root_factory``, ``zcml_file``, ``authentication_policy``, + ``authorization_policy``, ``renderers`` and ``debug_logger``. If the ``registry`` argument is passed as a non-``None`` value, it must be an instance of the :mod:`repoze.bfg.registry.Registry` @@ -93,11 +95,46 @@ class Configurator(object): argument, into absolute paths. If ``None`` is passed (the default), the package is assumed to be the Python package in which the *caller* of the ``Configurator`` constructor lives. + + If the ``settings`` argument is passed, it should be a Python + dictionary representing the deployment settings for this + application. These are later retrievable using the + ``repoze.bfg.settings.get_settings`` API. + + If the ``root_factory`` argument is passed, it should be an object + representing the default :term:`root factory` for your + application. If it is ``None``, a default root factory will be + used. + + If ``zcml_file`` is passed, it should be a filename relative to + the caller package, an absolute filename, or a :term:`resource + specification`. The file it refers to should contain + :term:`ZCML`. The ZCML represented in this file will be loaded. + + If ``authentication_policy`` is passed, it should be an instance + of an :term:`authentication policy`. + + If ``authorization_policy`` is passed, it should be an instance + of an :term:`authorization policy`. + + .. note:: A ``ConfigurationError`` will be raised if an + authorization policy is supplied without authentication policy + also being supplied (authorization requires authentication). + + If ``renderers`` is passed, it should be a list of tuples + representing a set of :term:`renderer` factories which should be + configured into this application. If it is not passed, a default + set of renderer factories is used. + + If ``debug_logger`` is not passed, a default debug logger that + logs to stderr will be used. If it is passed, it should be an + instance of a ``logging.Logger`` (PEP 282) class. + """ def __init__(self, registry=None, package=None, settings=None, - root_factory=None, debug_logger=None, zcml_file=None, + root_factory=None, zcml_file=None, authentication_policy=None, authorization_policy=None, - renderers=DEFAULT_RENDERERS): + renderers=DEFAULT_RENDERERS, debug_logger=None): self.package = package or caller_package() self.registry = registry if registry is None: |
