summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2010-09-05 22:39:20 +0000
committerChris McDonough <chrism@agendaless.com>2010-09-05 22:39:20 +0000
commitd89aee7640b1e01a7dda4e603e87461074fbbdd7 (patch)
tree16857b0c6fe0b0932903d13c10382732a7bf3b5f
parent91255626afd43d558cbc4eb74865ccdf0b629417 (diff)
downloadpyramid-d89aee7640b1e01a7dda4e603e87461074fbbdd7.tar.gz
pyramid-d89aee7640b1e01a7dda4e603e87461074fbbdd7.tar.bz2
pyramid-d89aee7640b1e01a7dda4e603e87461074fbbdd7.zip
- Each of the follow methods of the Configurator now allow the
below-named arguments to be passed as "dotted name strings" (e.g. "foo.bar.baz") rather than as actual implementation objects that must be imported: setup_registry root_factory, authentication_policy, authorization_policy, debug_logger, locale_negotiator, request_factory, renderer_globals_factory add_subscriber subscriber, iface derive_view view add_view view, for_, context, request_type, containment add_route() view, view_for, factory, for_, view_context scan package add_renderer factory set_forbidden_view view set_notfound_view view set_request_factory factory set_renderer_globals_factory() factory set_locale_negotiator negotiator testing_add_subscriber event_iface
-rw-r--r--CHANGES.txt46
-rw-r--r--TODO.txt77
-rw-r--r--docs/narr/events.rst3
-rw-r--r--docs/narr/firstapp.rst17
-rw-r--r--docs/narr/hooks.rst9
-rw-r--r--docs/narr/i18n.rst8
-rw-r--r--docs/narr/security.rst5
-rw-r--r--docs/narr/traversal.rst5
-rw-r--r--docs/narr/urldispatch.rst25
-rw-r--r--docs/narr/views.rst13
-rw-r--r--repoze/bfg/configuration.py186
-rw-r--r--repoze/bfg/tests/test_configuration.py225
-rw-r--r--repoze/bfg/view.py23
13 files changed, 460 insertions, 182 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 36c2afa7c..841eedd6c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -20,6 +20,52 @@ Features
``repoze.bfg.request`` API chapter. It can be used to influence
response values before a concrete response object has been created.
+- Each of the follow methods of the Configurator now allow the
+ below-named arguments to be passed as "dotted name strings"
+ (e.g. "foo.bar.baz") rather than as actual implementation objects
+ that must be imported:
+
+ setup_registry
+ root_factory, authentication_policy, authorization_policy,
+ debug_logger, locale_negotiator, request_factory,
+ renderer_globals_factory
+
+ add_subscriber
+ subscriber, iface
+
+ derive_view
+ view
+
+ add_view
+ view, for_, context, request_type, containment
+
+ add_route()
+ view, view_for, factory, for_, view_context
+
+ scan
+ package
+
+ add_renderer
+ factory
+
+ set_forbidden_view
+ view
+
+ set_notfound_view
+ view
+
+ set_request_factory
+ factory
+
+ set_renderer_globals_factory()
+ factory
+
+ set_locale_negotiator
+ negotiator
+
+ testing_add_subscriber
+ event_iface
+
Backwards Incompatibilities
---------------------------
diff --git a/TODO.txt b/TODO.txt
index c7668e496..acfad2e0c 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -63,80 +63,11 @@
- Raise an exception when a value in response_headerlist is not a
string or decide to encode.
-- These methods of Configurator should allow the arguments it receives
- named below them to be strings:
-
- __init__
-
- [ ] package
-
- setup_registry
-
- [ ] root_factory
- [.] authentication_policy
- [.] authorization_policy
- [ ] locale_negotiator
- [ ] request_factory
- [ ] renderer_globals_factory
-
- add_subscriber()
-
- [ ] subscriber
- [ ] iface
-
- derive_view()
-
- [ ] view
-
- add_view()
-
- [ ] view
- [ ] for_
- [ ] request_type
- [ ] containment
- [ ] context
-
- add_route()
-
- [.] view
- [.] view_for
- [ ] factory
- [.] for_
- [.] view_context
-
- scan()
-
- [ ] package
-
- add_renderer()
-
- [ ] factory
-
- set_forbidden_view()
-
- [ ] view
-
- set_notfound_view()
-
- [ ] view
-
- set_request_factory()
-
- [ ] factory
-
- set_renderer_globals_factory()
-
- [ ] factory
-
- set_locale_negotiator()
-
- [ ] negotiator
-
- testing_add_subscriber
-
- [ ] event_iface
-
- Make NewResponse event carry request.
- Change docs so that we dont use INewReponse, but instead just
NewResponse (likewise for INewRequest, etc).
+
+- Change "Cleaning up After a Request" in the urldispatch chapter to
+ use ``request.add_response_callback``.
+
diff --git a/docs/narr/events.rst b/docs/narr/events.rst
index 7f78139bb..6b46cfd9c 100644
--- a/docs/narr/events.rst
+++ b/docs/narr/events.rst
@@ -58,7 +58,8 @@ need to use ZCML for the same purpose:
The first argument to
:meth:`repoze.bfg.configuration.Configurator.add_subscriber` is the
- subscriber function; the second argument is the event type.
+ subscriber function (or a :term:`Python dotted name` which refers
+ to a subscriber callable); the second argument is the event type.
.. topic:: Configuring an Event Listener Through ZCML
diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst
index 01ad88704..deac69fda 100644
--- a/docs/narr/firstapp.rst
+++ b/docs/narr/firstapp.rst
@@ -235,14 +235,15 @@ arguments is known as a view configuration :term:`predicate`.
The line ``config.add_view(hello_world)`` registers the
``hello_world`` function as a view callable. The ``add_view`` method
-of a Configurator must be called with a view callable object as its
-first argument, so the first argument passed is the ``hello_world``
-function. This line calls ``add_view`` with a *default* value for the
-:term:`predicate` argument, named ``name``. The ``name`` predicate
-defaults to a value equalling the empty string (``''``). This means
-that we're instructing :mod:`repoze.bfg` to invoke the ``hello_world``
-view callable when the :term:`view name` is the empty string. We'll
-learn in later chapters what a :term:`view name` is, and under which
+of a Configurator must be called with a view callable object or a
+:term:`dotted Python name` as its first argument, so the first
+argument passed is the ``hello_world`` function. This line calls
+``add_view`` with a *default* value for the :term:`predicate`
+argument, named ``name``. The ``name`` predicate defaults to a value
+equalling the empty string (``''``). This means that we're
+instructing :mod:`repoze.bfg` to invoke the ``hello_world`` view
+callable when the :term:`view name` is the empty string. We'll learn
+in later chapters what a :term:`view name` is, and under which
circumstances a request will have a view name that is the empty
string; in this particular application, it means that the
``hello_world`` view callable will be invoked when the root URL ``/``
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 70e3f3759..b774c2012 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -351,7 +351,8 @@ request object.
The class (aka "factory") that :mod:`repoze.bfg` uses to create a
request object instance can be changed by passing a
``request_factory`` argument to the constructor of the
-:term:`configurator`.
+:term:`configurator`. This argument can be either a callable or a
+:term:`Python dotted name` representing a callable.
.. code-block:: python
:linenos:
@@ -392,7 +393,7 @@ method:
pass
config = Configurator()
- config.set_request_factory(MyRequestFactory)
+ config.set_request_factory(MyRequest)
.. _adding_renderer_globals:
@@ -411,7 +412,9 @@ renderer.
A callback that :mod:`repoze.bfg` will call every time a renderer is
invoked can be added by passing a ``renderer_globals_factory``
-argument to the constructor of the :term:`configurator`.
+argument to the constructor of the :term:`configurator`. This
+callback can either be a callable object or a :term:`Python dotted
+name` representing such a callable.
.. code-block:: python
:linenos:
diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst
index db1414cce..f456fe03c 100644
--- a/docs/narr/i18n.rst
+++ b/docs/narr/i18n.rst
@@ -1013,10 +1013,10 @@ configuration using either imperative configuration or ZCML.
.. topic:: Using Imperative Configuration
- Pass an object which can act as the negotiator as the
- ``locale_negotiator`` argument of the
- :class:`repoze.bfg.configuration.Configurator` instance during
- application startup.
+ Pass an object which can act as the negotiator (or a :term:`Python
+ dotted name` referring to the object) as the ``locale_negotiator``
+ argument of the :class:`repoze.bfg.configuration.Configurator`
+ instance during application startup.
For example:
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 13e6d632a..3b1de27ad 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -110,6 +110,11 @@ For example:
config = Configurator(authentication_policy=authentication_policy,
authorization_policy=authorization_policy)
+.. note:: the ``authentication_policy`` and ``authorization_policy``
+ arguments may also be passed to the Configurator as :ref:`dotted
+ Python name` values, each representing the dotted name path to a
+ suitable implementation global defined at Python module scope.
+
The above configuration enables 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
diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst
index a99df7ec8..bcad6dd07 100644
--- a/docs/narr/traversal.rst
+++ b/docs/narr/traversal.rst
@@ -183,7 +183,10 @@ Using the ``root_factory`` argument to a
:class:`repoze.bfg.configuration.Configurator` constructor tells your
:mod:`repoze.bfg` application to call this root factory to generate a
root object whenever a request enters the application. This root
-factory is also known as the global root factory.
+factory is also known as the global root factory. A root factory can
+alternately be passed to the ``Configurator`` as a :term:`dotted
+Python name` which refers to a root factory object defined in a
+different module.
A root factory is passed a :term:`request` object and it is expected
to return an object which represents the root of the object graph.
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 07e96763d..9779113e5 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -448,11 +448,11 @@ represent neither predicates nor view configuration information.
application.
``factory``
- A reference to a Python object (often a function or a class) that
- will generate a :mod:`repoze.bfg` :term:`context` object when this
- route matches. For example, ``mypackage.models.MyFactoryClass``. If
- this argument is not specified, the traversal root factory will be
- used.
+ A Python object (often a function or a class) or a :term:`Python
+ dotted name` to such an object that will generate a
+ :mod:`repoze.bfg` :term:`context` object when this route
+ matches. For example, ``mypackage.models.MyFactoryClass``. If this
+ argument is not specified, the traversal root factory will be used.
``traverse``
If you would like to cause the :term:`context` to be something other
@@ -573,15 +573,16 @@ represent neither predicates nor view configuration information.
**View-Related Arguments**
``view``
- A reference to a Python object that will be used as a view callable
- when this route matches. e.g. ``mypackage.views.my_view``.
+ A Python object or a :term:`dotted Python name` to such an object
+ that will be used as a view callable when this route
+ matches. e.g. ``mypackage.views.my_view``.
``view_context``
- A reference to a class or an :term:`interface` that the
- :term:`context` of the view should match for the view named by the
- route to be used. This argument is only useful if the ``view``
- attribute is used. If this attribute is not specified, the default
- (``None``) will be used.
+ A class or an :term:`interface` (or a :term:`dotted Python name` to
+ such an object) that the :term:`context` of the view should match
+ for the view named by the route to be used. This argument is only
+ useful if the ``view`` attribute is used. If this attribute is not
+ specified, the default (``None``) will be used.
If the ``view`` argument is not provided, this argument has
no effect.
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index eebaa63de..4d60f8a9e 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -620,7 +620,8 @@ For example, to add a renderer which renders views which have a
The first argument is the renderer name.
The second argument is a reference to an implementation of a
- :term:`renderer factory`.
+ :term:`renderer factory` or a :term:`dotted Python name` referring
+ to such an object.
Adding a New Renderer
+++++++++++++++++++++
@@ -1537,8 +1538,8 @@ Or replaces the need to add this imperative configuration stanza:
.. ignore-next-block
.. code-block:: python
- config.add_view(name='my_view', request_method='POST', context=MyModel,
- permission='read')
+ config.add_view('.views.my_view', name='my_view', request_method='POST',
+ context=MyModel, permission='read')
All arguments to ``bfg_view`` may be omitted. For example:
@@ -1748,6 +1749,12 @@ example:
# repoze.bfg.configuration.Configurator class
config.add_view(hello_world, name='hello.html')
+The first argument, ``view``, is required. It must either be a Python
+object which is the view itself or a :term:`dotted Python name` to
+such an object. All other arguments are optional. See
+:meth:`repoze.bfg.configuration.Configurator.add_view` for more
+information.
+
.. index::
single: model interfaces
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py
index 90336fe21..4ea52c571 100644
--- a/repoze/bfg/configuration.py
+++ b/repoze/bfg/configuration.py
@@ -126,15 +126,17 @@ class Configurator(object):
:func:`repoze.bfg.settings.get_settings` APIs.
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.
+ representing the default :term:`root factory` for your application
+ or a :term:`dotted Python name` to same. If it is ``None``, a
+ default root factory will be used.
If ``authentication_policy`` is passed, it should be an instance
- of an :term:`authentication policy`.
+ of an :term:`authentication policy` or a :term:`dotted Python
+ name` to same.
- If ``authorization_policy`` is passed, it should be an instance
- of an :term:`authorization policy`.
+ If ``authorization_policy`` is passed, it should be an instance of
+ an :term:`authorization policy` or a :term:`dotted Python name` to
+ same.
.. note:: A ``ConfigurationError`` will be raised when an
authorization policy is supplied without also supplying an
@@ -142,18 +144,21 @@ class Configurator(object):
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.
+ configured into this application (each tuple representing a set of
+ positional values that should be passed to
+ :meth:`repoze.bfg.configuration.Configurator.add_renderer`). 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 the :class:`logging.Logger` (PEP 282) standard library
- class. The debug logger is used by :mod:`repoze.bfg` itself to
- log warnings and authorization debugging information.
+ class or a :term:`dotted Python name` to same. The debug logger
+ is used by :mod:`repoze.bfg` itself to log warnings and
+ authorization debugging information.
- If ``locale_negotiator`` is passed, it should be a
- :term:`locale negotiator` implementation. See
- :ref:`custom_locale_negotiator`.
+ If ``locale_negotiator`` is passed, it should be a :term:`locale
+ negotiator` implementation or a :term:`dotted Python name` to
+ same. See :ref:`custom_locale_negotiator`.
"""
manager = manager # for testing injection
@@ -201,6 +206,7 @@ class Configurator(object):
""" Add a :term:`root factory` to the current configuration
state. If the ``factory`` argument is ``None`` a default root
factory will be registered."""
+ factory = self.maybe_dotted(factory)
if factory is None:
factory = DefaultRootFactory
self.registry.registerUtility(factory, IRootFactory)
@@ -209,11 +215,14 @@ class Configurator(object):
def _set_authentication_policy(self, policy, _info=u''):
""" Add a :mod:`repoze.bfg` :term:`authentication policy` to
the current configuration."""
+ policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthenticationPolicy, info=_info)
def _set_authorization_policy(self, policy, _info=u''):
""" Add a :mod:`repoze.bfg` :term:`authorization policy` to
- the current configuration state."""
+ the current configuration state (also accepts a :term:`dotted
+ Python name`."""
+ policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthorizationPolicy, info=_info)
def _make_spec(self, path_or_spec):
@@ -230,6 +239,7 @@ class Configurator(object):
attr=None, renderer_name=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
phash=DEFAULT_PHASH):
+ view = self.maybe_dotted(view)
authn_policy = self.registry.queryUtility(IAuthenticationPolicy)
authz_policy = self.registry.queryUtility(IAuthorizationPolicy)
settings = self.registry.queryUtility(ISettings)
@@ -338,6 +348,7 @@ class Configurator(object):
self._fix_registry()
self._set_settings(settings)
self._set_root_factory(root_factory)
+ debug_logger = self.maybe_dotted(debug_logger)
if debug_logger is None:
debug_logger = make_stream_logger('repoze.bfg.debug', sys.stderr)
registry = self.registry
@@ -352,10 +363,13 @@ class Configurator(object):
self.add_view(default_exceptionresponse_view,
context=IExceptionResponse)
if locale_negotiator:
+ locale_negotiator = self.maybe_dotted(locale_negotiator)
registry.registerUtility(locale_negotiator, ILocaleNegotiator)
if request_factory:
+ request_factory = self.maybe_dotted(request_factory)
self.set_request_factory(request_factory)
if renderer_globals_factory:
+ renderer_globals_factory=self.maybe_dotted(renderer_globals_factory)
self.set_renderer_globals_factory(renderer_globals_factory)
# getSiteManager is a unit testing dep injection
@@ -404,8 +418,10 @@ class Configurator(object):
def derive_view(self, view, attr=None, renderer=None):
"""
+
Create a :term:`view callable` using the function, instance,
- or class provided as ``view`` object.
+ or class (or :term:`dotted Python name` referring to the same)
+ provided as ``view`` object.
This is API is useful to framework extenders who create
pluggable systems which need to register 'proxy' view
@@ -453,6 +469,9 @@ class Configurator(object):
that accepts no arguments that returns a :term:`response`
object.
+ - A :term:`dotted Python name` which refers to any of the
+ kinds of objects above.
+
This API returns a callable which accepts the arguments
``context, request`` and which returns the result of calling
the provided ``view`` object.
@@ -463,25 +482,30 @@ class Configurator(object):
effectively defaults to ``__call__``. See
:ref:`class_as_view` for more information.
- The ``renderer`` keyword argument, if supplies, causes the
- returned callable to use a :term:`renderer` to convert the
- user-supplied view result to a :term:`response` object. If a
- ``renderer`` argument is not supplied, the user-supplied view
- must itself return a :term:`response` object.
- """
+ The ``renderer`` keyword argument should be a renderer
+ name. If supplied, it will cause the returned callable to use
+ a :term:`renderer` to convert the user-supplied view result to
+ a :term:`response` object. If a ``renderer`` argument is not
+ supplied, the user-supplied view must itself return a
+ :term:`response` object. """
return self._derive_view(view, attr=attr, renderer_name=renderer)
def add_subscriber(self, subscriber, iface=None, info=u''):
"""Add an event :term:`subscriber` for the event stream
implied by the supplied ``iface`` interface. The
- ``subscriber`` argument represents a callable object; it will
- be called with a single object ``event`` whenever
+ ``subscriber`` argument represents a callable object (or a
+ :ref:`Python dotted name` which identifies a callable); it
+ will be called with a single object ``event`` whenever
:mod:`repoze.bfg` emits an :term:`event` associated with the
- ``iface``. Using the default ``iface`` value, ``None`` will
- cause the subscriber to be registered for all event types. See
- :ref:`events_chapter` for more information about events and
- subscribers."""
+ ``iface``, which may be an :term:`interface` or a class or a
+ :term:`dotted Python name` to a global object representing an
+ interface or a class. Using the default ``iface`` value,
+ ``None`` will cause the subscriber to be registered for all
+ event types. See :ref:`events_chapter` for more information
+ about events and subscribers."""
+ dotted = self.maybe_dotted
+ subscriber, iface = dotted(subscriber), dotted(iface)
if iface is None:
iface = (Interface,)
if not isinstance(iface, (tuple, list)):
@@ -592,8 +616,9 @@ class Configurator(object):
view
- A reference to a :term:`view callable`. This argument is
- required unless a ``renderer`` argument also exists. If a
+ A :term:`view callable` or a :term:`dotted Python name`
+ which refers to a view callable. This argument is required
+ unless a ``renderer`` argument also exists. If a
``renderer`` argument is passed, and a ``view`` argument is
not provided, the view callable defaults to a callable that
returns an empty dictionary (see
@@ -685,8 +710,9 @@ class Configurator(object):
context
- An object representing Python class that the :term:`context`
- must be an instance of, *or* the :term:`interface` that the
+ An object or a :term:`dotted Python name` referring to an
+ interface or class object that the :term:`context` must be
+ an instance of, *or* the :term:`interface` that the
:term:`context` must provide in order for this view to be
found and called. This predicate is true when the
:term:`context` is an instance of the represented class or
@@ -743,11 +769,11 @@ class Configurator(object):
containment
- This value should be a reference to a Python class or
- :term:`interface` that a parent object in the
- :term:`lineage` must provide in order for this view to be
- found and called. The nodes in your object graph must be
- "location-aware" to use this feature. See
+ This value should be a Python class or :term:`interface` or
+ a :term:`dotted Python name` to such an object that a parent
+ object in the :term:`lineage` must provide in order for this
+ view to be found and called. The nodes in your object graph
+ must be "location-aware" to use this feature. See
:ref:`location_aware` for more information about
location-awareness.
@@ -814,6 +840,10 @@ class Configurator(object):
.. note:: This feature is new as of :mod:`repoze.bfg` 1.2.
"""
+ view = self.maybe_dotted(view)
+ context = self.maybe_dotted(context)
+ for_ = self.maybe_dotted(for_)
+ containment = self.maybe_dotted(containment)
if not view:
if renderer:
@@ -829,6 +859,7 @@ class Configurator(object):
request_type = None
if request_type is not None:
+ request_type = self.maybe_dotted(request_type)
if not IInterface.providedBy(request_type):
raise ConfigurationError(
'request_type must be an interface, not %s' % request_type)
@@ -1014,7 +1045,8 @@ class Configurator(object):
factory
- A reference to a Python object (often a function or a class)
+ A Python object (often a function or a class) or a
+ :term:`dotted Python name` which refers to the same object
that will generate a :mod:`repoze.bfg` :term:`context`
object when this route matches. For example,
``mypackage.models.MyFactoryClass``. If this argument is
@@ -1174,17 +1206,18 @@ class Configurator(object):
view
- A reference to a Python object that will be used as a view
- callable when this route
+ A Python object or :term:`dotted Python name` to the same
+ object that will be used as a view callable when this route
matches. e.g. ``mypackage.views.my_view``.
view_context
- A reference to a class or an :term:`interface` that the
- :term:`context` of the view should match for the view named
- by the route to be used. This argument is only useful if
- the ``view`` attribute is used. If this attribute is not
- specified, the default (``None``) will be used.
+ A class or an :term:`interface` or :term:`dotted Python
+ name` to the same object which the :term:`context` of the
+ view should match for the view named by the route to be
+ used. This argument is only useful if the ``view``
+ attribute is used. If this attribute is not specified, the
+ default (``None``) will be used.
If the ``view`` argument is not provided, this argument has
no effect.
@@ -1299,6 +1332,7 @@ class Configurator(object):
if mapper is None:
mapper = RoutesMapper()
self.registry.registerUtility(mapper, IRoutesMapper)
+ factory = self.maybe_dotted(factory)
return mapper.connect(path, name, factory, predicates=predicates)
def scan(self, package=None, categories=None, _info=u''):
@@ -1307,9 +1341,10 @@ class Configurator(object):
:class:`repoze.bfg.view.bfg_view`. Any decorated object found
will influence the current configuration state.
- The ``package`` argument should be a reference to a Python
- :term:`package` or module object. If ``package`` is ``None``,
- the package of the *caller* is used.
+ The ``package`` argument should be a Python :term:`package` or
+ module object (or a :term:`dotted Python name` which refers to
+ such a package or module). If ``package`` is ``None``, the
+ package of the *caller* is used.
The ``categories`` argument, if provided, should be the
:term:`Venusian` 'scan categories' to use during scanning.
@@ -1328,6 +1363,7 @@ class Configurator(object):
(e.g. ``('bfg', 'myframework')``) to limit the decorators
called to the set of categories required.
"""
+ package = self.maybe_dotted(package)
if package is None: # pragma: no cover
package = caller_package()
@@ -1335,13 +1371,15 @@ class Configurator(object):
scanner.scan(package, categories=categories)
def add_renderer(self, name, factory, _info=u''):
- """ Add a :mod:`repoze.bfg` :term:`renderer` factory to the current
- configuration state.
+ """
+ Add a :mod:`repoze.bfg` :term:`renderer` factory to the
+ current configuration state.
The ``name`` argument is the renderer name.
The ``factory`` argument is Python reference to an
- implementation of a :term:`renderer` factory.
+ implementation of a :term:`renderer` factory or a
+ :term:`dotted Python name` to same.
Note that this function must be called *before* any
``add_view`` invocation that names the renderer name as an
@@ -1350,6 +1388,7 @@ class Configurator(object):
in the sequence of renderers passed as ``renderer`` than it is
to use this method.
"""
+ factory = self.maybe_dotted(factory)
self.registry.registerUtility(
factory, IRendererFactory, name=name, info=_info)
@@ -1423,7 +1462,8 @@ class Configurator(object):
be found. The exception causing the registered view to be
called is however still available as ``request.exception``.
- The ``view`` argument should be a :term:`view callable`.
+ The ``view`` argument should be a :term:`view callable` or a
+ :term:`dotted Python name` which refers to a view callable.
The ``attr`` argument should be the attribute of the view
callable used to retrieve the response (see the ``add_view``
@@ -1468,7 +1508,8 @@ class Configurator(object):
be found. The exception causing the registered view to be
called is however still available as ``request.exception``.
- The ``view`` argument should be a :term:`view callable`.
+ The ``view`` argument should be a :term:`view callable` or a
+ :term:`dotted Python name` which refers to a view callable.
The ``attr`` argument should be the attribute of the view
callable used to retrieve the response (see the ``add_view``
@@ -1493,31 +1534,36 @@ class Configurator(object):
wrapper=wrapper, _info=_info)
def set_request_factory(self, factory):
- """ The object passed as ``factory`` will be used by the
- :mod:`repoze.bfg` router to create all request objects.
- This factory object must have the same methods and attributes
- as the :class:`repoze.bfg.request.Request` class (particularly
- ``__call__`` and ``blank``).
+ """ The object passed as ``factory`` should be an object (or a
+ :term:`dotted Python name` which refers to an object) which
+ will be used by the :mod:`repoze.bfg` router to create all
+ request objects. This factory object must have the same
+ methods and attributes as the
+ :class:`repoze.bfg.request.Request` class (particularly
+ ``__call__``, and ``blank``).
.. note:: Using the :meth:``request_factory`` argument to the
:class:`repoze.bfg.configuration.Configurator` constructor
can be used to achieve the same purpose.
"""
+ factory = self.maybe_dotted(factory)
self.registry.registerUtility(factory, IRequestFactory)
def set_renderer_globals_factory(self, factory):
- """ The object passed as ``factory`` will be used by the
- :mod:`repoze.bfg` rendering machinery as a renderers global
- factory (see :ref:`adding_renderer_globals`). The factory
- must return a dictionary of items that will be merged intto
- the *system* dictionary passed in to every renderer used by
- the application.
+ """ The object passed as ``factory`` should be an object (or a
+ :term:`dotted Python name` which refers to an object) that
+ will be used by the :mod:`repoze.bfg` rendering machinery as a
+ renderers global factory (see :ref:`adding_renderer_globals`).
+ The factory must return a dictionary of items that will be
+ merged intto the *system* dictionary passed in to every
+ renderer used by the application.
.. note:: Using the :meth:`renderer_globals_factory`
argument to the
:class:`repoze.bfg.configuration.Configurator` constructor
can be used to achieve the same purpose.
"""
+ factory = self.maybe_dotted(factory)
self.registry.registerUtility(factory, IRendererGlobalsFactory)
def set_locale_negotiator(self, negotiator):
@@ -1525,10 +1571,14 @@ class Configurator(object):
Set the :term:`locale negotiator` for this application. The
:term:`locale negotiator` is a callable which accepts a
:term:`request` object and which returns a :term:`locale
- name`. Later calls to this method override earlier calls;
- there can be only one locale negotiator active at a time
- within an application. See :ref:`activating_translation` for
- more information.
+ name`. The ``negotiator`` argument should be the locale
+ negotiator implementation or a :term:`dotted Python` name
+ which refers to such an implementation.
+
+ Later calls to this method override earlier calls; there can
+ be only one locale negotiator active at a time within an
+ application. See :ref:`activating_translation` for more
+ information.
.. note: This API is new as of :mod:`repoze.bfg` version 1.3.
@@ -1536,6 +1586,7 @@ class Configurator(object):
the :class:`repoze.bfg.configuration.Configurator`
constructor can be used to achieve the same purpose.
"""
+ negotiator = self.maybe_dotted(negotiator)
self.registry.registerUtility(negotiator, ILocaleNegotiator)
def add_translation_dirs(self, *specs):
@@ -1762,6 +1813,7 @@ class Configurator(object):
The default value of ``event_iface`` (``None``) implies a
subscriber registered for *any* kind of event.
"""
+ event_iface = self.maybe_dotted(event_iface)
L = []
def subscriber(*event):
L.extend(event)
diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py
index 159429a06..be9cd942c 100644
--- a/repoze/bfg/tests/test_configuration.py
+++ b/repoze/bfg/tests/test_configuration.py
@@ -306,6 +306,16 @@ class ConfiguratorTests(unittest.TestCase):
result = reg.getUtility(IDebugLogger)
self.assertEqual(logger, result)
+ def test_setup_registry_debug_logger_dottedname(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import IDebugLogger
+ reg = Registry()
+ config = self._makeOne(reg)
+ config.setup_registry(debug_logger='repoze.bfg.tests')
+ result = reg.getUtility(IDebugLogger)
+ import repoze.bfg.tests
+ self.assertEqual(result, repoze.bfg.tests)
+
def test_setup_registry_authentication_policy(self):
from repoze.bfg.registry import Registry
from repoze.bfg.interfaces import IAuthenticationPolicy
@@ -316,6 +326,28 @@ class ConfiguratorTests(unittest.TestCase):
result = reg.getUtility(IAuthenticationPolicy)
self.assertEqual(policy, result)
+ def test_setup_registry_authentication_policy_dottedname(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import IAuthenticationPolicy
+ reg = Registry()
+ config = self._makeOne(reg)
+ config.setup_registry(authentication_policy='repoze.bfg.tests')
+ result = reg.getUtility(IAuthenticationPolicy)
+ import repoze.bfg.tests
+ self.assertEqual(result, repoze.bfg.tests)
+
+ def test_setup_registry_authorization_policy_dottedname(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import IAuthorizationPolicy
+ reg = Registry()
+ config = self._makeOne(reg)
+ dummy = object()
+ config.setup_registry(authentication_policy=dummy,
+ authorization_policy='repoze.bfg.tests')
+ result = reg.getUtility(IAuthorizationPolicy)
+ import repoze.bfg.tests
+ self.assertEqual(result, repoze.bfg.tests)
+
def test_setup_registry_authorization_policy_only(self):
from repoze.bfg.registry import Registry
from repoze.bfg.exceptions import ConfigurationError
@@ -334,6 +366,25 @@ class ConfiguratorTests(unittest.TestCase):
config.setup_registry()
self.failUnless(reg.getUtility(IRootFactory))
+ def test_setup_registry_dottedname_root_factory(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import IRootFactory
+ reg = Registry()
+ config = self._makeOne(reg)
+ import repoze.bfg.tests
+ config.setup_registry(root_factory='repoze.bfg.tests')
+ self.assertEqual(reg.getUtility(IRootFactory), repoze.bfg.tests)
+
+ def test_setup_registry_locale_negotiator_dottedname(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import ILocaleNegotiator
+ reg = Registry()
+ config = self._makeOne(reg)
+ import repoze.bfg.tests
+ config.setup_registry(locale_negotiator='repoze.bfg.tests')
+ utility = reg.getUtility(ILocaleNegotiator)
+ self.assertEqual(utility, repoze.bfg.tests)
+
def test_setup_registry_locale_negotiator(self):
from repoze.bfg.registry import Registry
from repoze.bfg.interfaces import ILocaleNegotiator
@@ -354,6 +405,16 @@ class ConfiguratorTests(unittest.TestCase):
utility = reg.getUtility(IRequestFactory)
self.assertEqual(utility, factory)
+ def test_setup_registry_request_factory_dottedname(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import IRequestFactory
+ reg = Registry()
+ config = self._makeOne(reg)
+ import repoze.bfg.tests
+ config.setup_registry(request_factory='repoze.bfg.tests')
+ utility = reg.getUtility(IRequestFactory)
+ self.assertEqual(utility, repoze.bfg.tests)
+
def test_setup_registry_renderer_globals_factory(self):
from repoze.bfg.registry import Registry
from repoze.bfg.interfaces import IRendererGlobalsFactory
@@ -364,6 +425,16 @@ class ConfiguratorTests(unittest.TestCase):
utility = reg.getUtility(IRendererGlobalsFactory)
self.assertEqual(utility, factory)
+ def test_setup_registry_renderer_globals_factory_dottedname(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import IRendererGlobalsFactory
+ reg = Registry()
+ config = self._makeOne(reg)
+ import repoze.bfg.tests
+ config.setup_registry(renderer_globals_factory='repoze.bfg.tests')
+ utility = reg.getUtility(IRendererGlobalsFactory)
+ self.assertEqual(utility, repoze.bfg.tests)
+
def test_setup_registry_alternate_renderers(self):
from repoze.bfg.registry import Registry
from repoze.bfg.interfaces import IRendererFactory
@@ -445,6 +516,18 @@ class ConfiguratorTests(unittest.TestCase):
config.registry.notify(object())
self.assertEqual(len(L), 1)
+ def test_add_subscriber_dottednames(self):
+ import repoze.bfg.tests
+ from repoze.bfg.interfaces import INewRequest
+ config = self._makeOne()
+ config.add_subscriber('repoze.bfg.tests',
+ 'repoze.bfg.interfaces.INewRequest')
+ handlers = list(config.registry.registeredHandlers())
+ self.assertEqual(len(handlers), 1)
+ handler = handlers[0]
+ self.assertEqual(handler.handler, repoze.bfg.tests)
+ self.assertEqual(handler.required, (INewRequest,))
+
def test_add_object_event_subscriber(self):
from zope.interface import implements
from zope.interface import Interface
@@ -538,7 +621,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertRaises(ConfigurationError, config.add_view, view, '', None,
None, True, True)
- def test_add_view_with_request_type_string(self):
+ def test_add_view_with_request_type_methodname_string(self):
view = lambda *arg: 'OK'
config = self._makeOne()
config.add_view(view=view, request_type='GET')
@@ -551,6 +634,20 @@ class ConfiguratorTests(unittest.TestCase):
result = wrapper(None, request)
self.assertEqual(result, 'OK')
+ def test_add_view_with_request_type(self):
+ from zope.interface import directlyProvides
+ from repoze.bfg.interfaces import IRequest
+ view = lambda *arg: 'OK'
+ config = self._makeOne()
+ config.add_view(view=view,
+ request_type='repoze.bfg.interfaces.IRequest')
+ wrapper = self._getViewCallable(config)
+ request = DummyRequest()
+ self._assertNotFound(wrapper, None, request)
+ directlyProvides(request, IRequest)
+ result = wrapper(None, request)
+ self.assertEqual(result, 'OK')
+
def test_add_view_view_callable_None_with_renderer(self):
config = self._makeOne()
self._registerRenderer(config, name='dummy')
@@ -568,6 +665,12 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(wrapper.__name__, view.__name__)
self.assertEqual(wrapper.__doc__, view.__doc__)
+ def test_add_view_view_callable_dottedname(self):
+ config = self._makeOne()
+ config.add_view(view='repoze.bfg.tests.test_configuration.dummy_view')
+ wrapper = self._getViewCallable(config)
+ self.assertEqual(wrapper(None, None), 'OK')
+
def test_add_view_with_function_callable(self):
view = lambda *arg: 'OK'
config = self._makeOne()
@@ -654,6 +757,22 @@ class ConfiguratorTests(unittest.TestCase):
wrapper = self._getViewCallable(config, IDummy)
self.assertEqual(wrapper, view)
+ def test_add_view_context_as_dottedname(self):
+ view = lambda *arg: 'OK'
+ config = self._makeOne()
+ config.add_view(context='repoze.bfg.tests.test_configuration.IDummy',
+ view=view)
+ wrapper = self._getViewCallable(config, IDummy)
+ self.assertEqual(wrapper, view)
+
+ def test_add_view_for__as_dottedname(self):
+ view = lambda *arg: 'OK'
+ config = self._makeOne()
+ config.add_view(for_='repoze.bfg.tests.test_configuration.IDummy',
+ view=view)
+ wrapper = self._getViewCallable(config, IDummy)
+ self.assertEqual(wrapper, view)
+
def test_add_view_for_as_class(self):
# ``for_`` is older spelling for ``context``
from zope.interface import implementedBy
@@ -1487,6 +1606,18 @@ class ConfiguratorTests(unittest.TestCase):
context = DummyContext()
self._assertNotFound(wrapper, context, None)
+ def test_add_view_with_containment_dottedname(self):
+ from zope.interface import directlyProvides
+ view = lambda *arg: 'OK'
+ config = self._makeOne()
+ config.add_view(
+ view=view,
+ containment='repoze.bfg.tests.test_configuration.IDummy')
+ wrapper = self._getViewCallable(config)
+ context = DummyContext()
+ directlyProvides(context, IDummy)
+ self.assertEqual(wrapper(context, None), 'OK')
+
def test_add_view_with_path_info_badregex(self):
from repoze.bfg.exceptions import ConfigurationError
view = lambda *arg: 'OK'
@@ -1590,6 +1721,19 @@ class ConfiguratorTests(unittest.TestCase):
self._assertRoute(config, 'name', 'path')
self.assertEqual(route.name, 'name')
+ def test_add_route_with_factory(self):
+ config = self._makeOne()
+ factory = object()
+ route = config.add_route('name', 'path', factory=factory)
+ self.assertEqual(route.factory, factory)
+
+ def test_add_route_with_factory_dottedname(self):
+ config = self._makeOne()
+ route = config.add_route(
+ 'name', 'path',
+ factory='repoze.bfg.tests.test_configuration.dummyfactory')
+ self.assertEqual(route.factory, dummyfactory)
+
def test_add_route_with_xhr(self):
config = self._makeOne()
config.add_route('name', 'path', xhr=True)
@@ -1942,6 +2086,14 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(ILocaleNegotiator),
negotiator)
+ def test_set_locale_negotiator_dottedname(self):
+ from repoze.bfg.interfaces import ILocaleNegotiator
+ config = self._makeOne()
+ config.set_locale_negotiator(
+ 'repoze.bfg.tests.test_configuration.dummyfactory')
+ self.assertEqual(config.registry.getUtility(ILocaleNegotiator),
+ dummyfactory)
+
def test_set_request_factory(self):
from repoze.bfg.interfaces import IRequestFactory
config = self._makeOne()
@@ -1949,6 +2101,30 @@ class ConfiguratorTests(unittest.TestCase):
config.set_request_factory(factory)
self.assertEqual(config.registry.getUtility(IRequestFactory), factory)
+ def test_set_request_factory_dottedname(self):
+ from repoze.bfg.interfaces import IRequestFactory
+ config = self._makeOne()
+ config.set_request_factory(
+ 'repoze.bfg.tests.test_configuration.dummyfactory')
+ self.assertEqual(config.registry.getUtility(IRequestFactory),
+ dummyfactory)
+
+ def test_set_renderer_globals_factory(self):
+ from repoze.bfg.interfaces import IRendererGlobalsFactory
+ config = self._makeOne()
+ factory = object()
+ config.set_renderer_globals_factory(factory)
+ self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory),
+ factory)
+
+ def test_set_renderer_globals_factory_dottedname(self):
+ from repoze.bfg.interfaces import IRendererGlobalsFactory
+ config = self._makeOne()
+ config.set_renderer_globals_factory(
+ 'repoze.bfg.tests.test_configuration.dummyfactory')
+ self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory),
+ dummyfactory)
+
def test_add_translation_dirs_missing_dir(self):
from repoze.bfg.exceptions import ConfigurationError
config = self._makeOne()
@@ -1997,6 +2173,13 @@ class ConfiguratorTests(unittest.TestCase):
self.failIf(result is view)
self.assertEqual(result(None, None), 'OK')
+ def test_derive_view_dottedname(self):
+ config = self._makeOne()
+ result = config.derive_view(
+ 'repoze.bfg.tests.test_configuration.dummy_view')
+ self.failIf(result is dummy_view)
+ self.assertEqual(result(None, None), 'OK')
+
def test_derive_view_with_renderer(self):
def view(request):
return 'OK'
@@ -2367,6 +2550,14 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'),
renderer)
+ def test_add_renderer_dottedname_factory(self):
+ from repoze.bfg.interfaces import IRendererFactory
+ config = self._makeOne()
+ import repoze.bfg.tests
+ config.add_renderer('name', 'repoze.bfg.tests')
+ self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'),
+ repoze.bfg.tests)
+
def test_scan_integration(self):
from zope.interface import alsoProvides
from repoze.bfg.interfaces import IRequest
@@ -2466,6 +2657,22 @@ class ConfiguratorTests(unittest.TestCase):
result = render_view_to_response(ctx, req, 'pod_notinit')
self.assertEqual(result, None)
+ def test_scan_integration_dottedname_package(self):
+ from zope.interface import alsoProvides
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.view import render_view_to_response
+ config = self._makeOne()
+ config.scan('repoze.bfg.tests.grokkedapp')
+
+ ctx = DummyContext()
+ req = DummyRequest()
+ alsoProvides(req, IRequest)
+ req.registry = config.registry
+
+ req.method = 'GET'
+ result = render_view_to_response(ctx, req, '')
+ self.assertEqual(result, 'grokked')
+
def test_testing_securitypolicy(self):
from repoze.bfg.testing import DummySecurityPolicy
config = self._makeOne()
@@ -2520,6 +2727,17 @@ class ConfiguratorTests(unittest.TestCase):
config.registry.notify(object())
self.assertEqual(len(L), 1)
+ def test_testing_add_subscriber_dottedname(self):
+ config = self._makeOne()
+ L = config.testing_add_subscriber(
+ 'repoze.bfg.tests.test_configuration.IDummy')
+ event = DummyEvent()
+ config.registry.notify(event)
+ self.assertEqual(len(L), 1)
+ self.assertEqual(L[0], event)
+ config.registry.notify(object())
+ self.assertEqual(len(L), 1)
+
def test_testing_add_subscriber_multiple(self):
config = self._makeOne()
L = config.testing_add_subscriber((Interface, IDummy))
@@ -3999,3 +4217,8 @@ class DummyStaticURLInfo:
def add(self, name, spec, **kw):
self.added.append((name, spec, kw))
+def dummy_view(request):
+ return 'OK'
+
+def dummyfactory(request):
+ """ """
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py
index 0273c1e72..2a154df87 100644
--- a/repoze/bfg/view.py
+++ b/repoze/bfg/view.py
@@ -200,9 +200,11 @@ class bfg_view(object):
``request_param``, ``containment``, ``xhr``, ``accept``,
``header`` and ``path_info``.
- If ``context`` is not supplied, the interface
- ``zope.interface.Interface`` (matching any context) is used. An alias
- for ``context`` is ``for_``.
+ ``context`` should be a Python object or :term:`dotted Python
+ name` representing the context type that must be found for this
+ view to be called. If ``context`` is not supplied, the interface
+ ``zope.interface.Interface`` (matching any context) is used. An
+ alias for ``context`` is ``for_``.
If ``permission`` is not supplied, no permission is registered for
this view (it's accessible by any caller).
@@ -248,12 +250,15 @@ class bfg_view(object):
right hand side of the expression (``123``) for the view to "match" the
current request.
- If ``containment`` is not supplied, this view will be called when
- the context of the request has any (or no) :term:`lineage`. If
- ``containment`` *is* supplied, it must be a class or
- :term:`interface`, denoting that the view 'matches' the current
- request only if any graph :term:`lineage` node possesses this
- class or interface.
+ ``containment`` should be a Python object or :term:`dotted Python
+ name` representing a class or interface type which must be found
+ as one of the context's location parents for this view to be
+ called. If ``containment`` is not supplied, this view will be
+ called when the context of the request has any (or no)
+ :term:`lineage`. If ``containment`` *is* supplied, it must be a
+ class or :term:`interface`, denoting that the view'matches' the
+ current request only if any graph :term:`lineage` node possesses
+ this class or interface.
If ``xhr`` is specified, it must be a boolean value. If the value
is ``True``, the view will only be invoked if the request's