summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt25
-rw-r--r--docs/api/configuration.rst4
-rw-r--r--docs/whatsnew-1.3.rst18
-rw-r--r--repoze/bfg/configuration.py302
-rw-r--r--repoze/bfg/path.py6
-rw-r--r--repoze/bfg/tests/test_configuration.py93
-rw-r--r--repoze/bfg/tests/test_path.py18
7 files changed, 332 insertions, 134 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index f5e64f18f..823bc7e38 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,31 @@
Next release
============
+Features
+--------
+
+- The Configurator now accepts a dotted name *string* to a package as
+ a ``package`` constructor argument. The ``package`` argument was
+ previously required to be a package *object* (not a dotted name
+ string).
+
+- The ``repoze.bfg.configuration.Configurator.with_package`` method
+ was added. This method returns a new Configurator using the same
+ application registry as the configurator object it is called
+ upon. The new configurator is created afresh with its ``package``
+ constructor argument set to the value passed to ``with_package``.
+ This feature will make it easier for future BFG versions to allow
+ dotted names as arguments in places where currently only object
+ references are allowed (the work to allow dotted names isntead of
+ object references everywhere has not yet been done, however).
+
+- The ``repoze.bfg.configuration.Configurator.maybe_dotted`` method
+ resolves a Python dotted name string supplied as its ``dotted``
+ argument to a global Python object. If the value cannot be
+ resolved, a ``repoze.bfg.configuration.ConfigurationError`` is
+ raised. If the value supplied as ``dotted`` is not a string, the
+ value is returned unconditionally without any resolution attempted.
+
Internal
--------
diff --git a/docs/api/configuration.rst b/docs/api/configuration.rst
index f555b18f0..ef0d7f631 100644
--- a/docs/api/configuration.rst
+++ b/docs/api/configuration.rst
@@ -22,6 +22,10 @@
.. automethod:: get_settings
+ .. automethod:: with_package
+
+ .. automethod:: maybe_dotted
+
.. automethod:: setup_registry(settings=None, root_factory=None, authentication_policy=None, renderers=DEFAULT_RENDERERS, debug_logger=None, locale_negotiator=None, request_factory=None, renderer_globals_factory=None)
.. automethod:: add_renderer(name, factory)
diff --git a/docs/whatsnew-1.3.rst b/docs/whatsnew-1.3.rst
index 07f63b9d6..f72a76e19 100644
--- a/docs/whatsnew-1.3.rst
+++ b/docs/whatsnew-1.3.rst
@@ -271,6 +271,24 @@ Minor Feature Additions
callable calling convention of ``(context, request)`` (``context``
will be the exception object).
+- The :class:`repoze.bfg.configuration.Configurator` constructor now
+ accepts a dotted name *string* to a package as a ``package``
+ argument. The ``package`` argument was previously required to be a
+ package *object* (not a dotted name string).
+
+- The :meth:`repoze.bfg.configuration.Configurator.with_package`
+ method was added. This method returns a new Configurator using the
+ same application registry as the configurator object it is called
+ upon. The new configurator is created afresh with its ``package``
+ constructor argument set to the value passed to ``with_package``.
+
+- The :meth:`repoze.bfg.configuration.Configurator.maybe_dotted`
+ method resolves a Python dotted name string supplied as its
+ ``dotted`` argument to a global Python object. If the value cannot
+ be resolved, a :exc:`repoze.bfg.configuration.ConfigurationError` is
+ raised. If the value supplied as ``dotted`` is not a string, the
+ value is returned unconditionally without any resolution attempted.
+
Backwards Incompatibilities
---------------------------
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py
index ff41b433e..59ae63989 100644
--- a/repoze/bfg/configuration.py
+++ b/repoze/bfg/configuration.py
@@ -3,6 +3,7 @@ import re
import sys
import threading
import inspect
+import pkg_resources
import venusian
@@ -57,6 +58,7 @@ from repoze.bfg.i18n import get_localizer
from repoze.bfg.log import make_stream_logger
from repoze.bfg.path import caller_package
from repoze.bfg.path import package_path
+from repoze.bfg.path import package_of
from repoze.bfg.registry import Registry
from repoze.bfg.request import route_request_iface
from repoze.bfg.resource import PackageOverrides
@@ -109,12 +111,13 @@ class Configurator(object):
are ignored.
If the ``package`` argument is passed, it must be a reference to a
- Python :term:`package` (e.g. ``sys.modules['thepackage']``). This
- value is used as a basis to convert relative paths passed to
- various configuration methods, such as methods which accept a
- ``renderer`` 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.
+ Python :term:`package` (e.g. ``sys.modules['thepackage']``) or a
+ :term:`dotted Python name` to same. This value is used as a basis
+ to convert relative paths passed to various configuration methods,
+ such as methods which accept a ``renderer`` 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
@@ -167,10 +170,15 @@ class Configurator(object):
locale_negotiator=None,
request_factory=None,
renderer_globals_factory=None):
- self.package = package or caller_package()
+ if package is None:
+ package = caller_package()
+ name_resolver = DottedNameResolver(package)
+ self.name_resolver = name_resolver
+ self.package_name = name_resolver.package_name
+ self.package = name_resolver.package
self.registry = registry
if registry is None:
- registry = Registry(self.package.__name__)
+ registry = Registry(self.package_name)
self.registry = registry
self.setup_registry(
settings=settings,
@@ -236,83 +244,13 @@ class Configurator(object):
def _make_spec(self, path_or_spec):
package, filename = resolve_resource_spec(path_or_spec,
- self.package.__name__)
+ self.package_name)
if package is None:
return filename # absolute filename
return '%s:%s' % (package, filename)
def _split_spec(self, path_or_spec):
- return resolve_resource_spec(path_or_spec, self.package.__name__)
-
- def derive_view(self, view, attr=None, renderer=None):
- """
- Create a :term:`view callable` using the function, instance,
- or class provided as ``view`` object.
-
- This is API is useful to framework extenders who create
- pluggable systems which need to register 'proxy' view
- callables for functions, instances, or classes which meet the
- requirements of being a :mod:`repoze.bfg` view callable. For
- example, a ``some_other_framework`` function in another
- framework may want to allow a user to supply a view callable,
- but he may want to wrap the view callable in his own before
- registering the wrapper as a :mod:`repoze.bfg` view callable.
- Because a :mod:`repoze.bfg` view callable can be any of a
- number of valid objects, the framework extender will not know
- how to call the user-supplied object. Running it through
- ``derive_view`` normalizes it to a callable which accepts two
- arguments: ``context`` and ``request``.
-
- For example:
-
- .. code-block:: python
-
- def some_other_framework(user_supplied_view):
- config = Configurator(reg)
- proxy_view = config.derive_view(user_supplied_view)
- def my_wrapper(context, request):
- do_something_that_mutates(request)
- return proxy_view(context, request)
- config.add_view(my_wrapper)
-
- The ``view`` object provided should be one of the following:
-
- - A function or another non-class callable object that accepts
- a :term:`request` as a single positional argument and which
- returns a :term:`response` object.
-
- - A function or other non-class callable object that accepts
- two positional arguments, ``context, request`` and which
- returns a :term:`response` object.
-
- - A class which accepts a single positional argument in its
- constructor named ``request``, and which has a ``__call__``
- method that accepts no arguments that returns a
- :term:`response` object.
-
- - A class which accepts two positional arguments named
- ``context, request``, and which has a ``__call__`` method
- that accepts no arguments that returns a :term:`response`
- object.
-
- This API returns a callable which accepts the arguments
- ``context, request`` and which returns the result of calling
- the provided ``view`` object.
-
- The ``attr`` keyword argument is most useful when the view
- object is a class. It names the method that should be used as
- the callable. If ``attr`` is not provided, the attribute
- 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.
- """
-
- return self._derive_view(view, attr=attr, renderer_name=renderer)
+ return resolve_resource_spec(path_or_spec, self.package_name)
def _derive_view(self, view, permission=None, predicates=(),
attr=None, renderer_name=None, wrapper_viewname=None,
@@ -372,6 +310,21 @@ class Configurator(object):
# API
+ def with_package(self, package):
+ """ Return a new Configurator instance with the same registry
+ as this configurator using the package supplied as the
+ ``package`` argument to the new configurator."""
+ return self.__class__(registry=self.registry, package=package)
+
+ def maybe_dotted(self, dotted):
+ """ Resolve the dotted name ``dotted`` to a global Python
+ object. If ``dotted`` is not a string, return it without
+ attempting to do any name resolution. If ``dotted`` is a
+ relative dotted name (e.g. ``.foo.bar``, consider it relative
+ to the ``package`` argument supplied to this Configurator's
+ constructor."""
+ return self.name_resolver.maybe_resolve(dotted)
+
def setup_registry(self, settings=None, root_factory=None,
authentication_policy=None, authorization_policy=None,
renderers=DEFAULT_RENDERERS, debug_logger=None,
@@ -462,6 +415,76 @@ class Configurator(object):
"""
return self.manager.pop()
+ def derive_view(self, view, attr=None, renderer=None):
+ """
+ Create a :term:`view callable` using the function, instance,
+ or class provided as ``view`` object.
+
+ This is API is useful to framework extenders who create
+ pluggable systems which need to register 'proxy' view
+ callables for functions, instances, or classes which meet the
+ requirements of being a :mod:`repoze.bfg` view callable. For
+ example, a ``some_other_framework`` function in another
+ framework may want to allow a user to supply a view callable,
+ but he may want to wrap the view callable in his own before
+ registering the wrapper as a :mod:`repoze.bfg` view callable.
+ Because a :mod:`repoze.bfg` view callable can be any of a
+ number of valid objects, the framework extender will not know
+ how to call the user-supplied object. Running it through
+ ``derive_view`` normalizes it to a callable which accepts two
+ arguments: ``context`` and ``request``.
+
+ For example:
+
+ .. code-block:: python
+
+ def some_other_framework(user_supplied_view):
+ config = Configurator(reg)
+ proxy_view = config.derive_view(user_supplied_view)
+ def my_wrapper(context, request):
+ do_something_that_mutates(request)
+ return proxy_view(context, request)
+ config.add_view(my_wrapper)
+
+ The ``view`` object provided should be one of the following:
+
+ - A function or another non-class callable object that accepts
+ a :term:`request` as a single positional argument and which
+ returns a :term:`response` object.
+
+ - A function or other non-class callable object that accepts
+ two positional arguments, ``context, request`` and which
+ returns a :term:`response` object.
+
+ - A class which accepts a single positional argument in its
+ constructor named ``request``, and which has a ``__call__``
+ method that accepts no arguments that returns a
+ :term:`response` object.
+
+ - A class which accepts two positional arguments named
+ ``context, request``, and which has a ``__call__`` method
+ that accepts no arguments that returns a :term:`response`
+ object.
+
+ This API returns a callable which accepts the arguments
+ ``context, request`` and which returns the result of calling
+ the provided ``view`` object.
+
+ The ``attr`` keyword argument is most useful when the view
+ object is a class. It names the method that should be used as
+ the callable. If ``attr`` is not provided, the attribute
+ 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.
+ """
+
+ 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
@@ -894,7 +917,7 @@ class Configurator(object):
if old_view is not None:
break
- is_exception_view = isexception(context)
+ isexc = isexception(context)
def regclosure():
if hasattr(view, '__call_permissive__'):
@@ -902,9 +925,10 @@ class Configurator(object):
else:
view_iface = IView
self.registry.registerAdapter(
- derived_view, (IViewClassifier, request_iface, context),
+ derived_view,
+ (IViewClassifier, request_iface, context),
view_iface, name, info=_info)
- if is_exception_view:
+ if isexc:
self.registry.registerAdapter(
derived_view,
(IExceptionViewClassifier, request_iface, context),
@@ -947,18 +971,19 @@ class Configurator(object):
self.registry.adapters.unregister(
(IViewClassifier, request_iface, r_context),
view_type, name=name)
- if is_exception_view:
+ if isexc:
self.registry.adapters.unregister(
(IExceptionViewClassifier, request_iface, r_context),
view_type, name=name)
self.registry.registerAdapter(
- multiview, (IViewClassifier, request_iface, context),
- IMultiView, name, info=_info)
- if is_exception_view:
+ multiview,
+ (IViewClassifier, request_iface, context),
+ IMultiView, name=name, info=_info)
+ if isexc:
self.registry.registerAdapter(
multiview,
(IExceptionViewClassifier, request_iface, context),
- IMultiView, name, info=_info)
+ IMultiView, name=name, info=_info)
def add_route(self,
name,
@@ -2385,11 +2410,11 @@ class DottedNameResolver(object):
Two dotted name styles are supported during deserialization:
- ``pkg_resources``-style dotted names where non-module attributes
- of a module are separated from the rest of the path using a ':'
+ of a package are separated from the rest of the path using a ':'
e.g. ``package.module:attr``.
- ``zope.dottedname``-style dotted names where non-module
- attributes of a module are separated from the rest of the path
+ attributes of a package are separated from the rest of the path
using a '.' e.g. ``package.module.attr``.
These styles can be used interchangeably. If the serialization
@@ -2398,42 +2423,79 @@ class DottedNameResolver(object):
resolution mechanism will be chosen.
The constructor accepts a single argument named ``package`` which
- should be a Python module or package object; it is used when
- *relative* dotted names are supplied to the ``__call__`` method.
- A dotted name which has a ``.`` (dot) or ``:`` (colon) as its
- first character is treated as relative. E.g. if ``.minidom`` is
- supplied to ``deserialize``, and the ``package`` argument to this
- type was passed the ``xml`` module object, the resulting import
- would be for ``xml.minidom``. If a relative package name is
- supplied to ``deserialize``, and no ``package`` was supplied to
- the constructor, an :exc:`repoze.bfg.ConfigurationError` error
- will be raised.
+ should be a one of:
+
+ - a Python module or package object
+
+ - A fully qualified (not relative) dotted name to a module or package
+
+ - The value ``None``
+
+ The ``package`` is used when relative dotted names are supplied to
+ the resolver's ``resolve`` and ``maybe_resolve`` methods. A
+ dotted name which has a ``.`` (dot) or ``:`` (colon) as its first
+ character is treated as relative.
+
+ If the value ``None`` is supplied as the package name, the
+ resolver will only be able to resolve fully qualified (not
+ relative) names. Any attempt to resolve a relative name when the
+ ``package`` is ``None`` will result in an
+ :exc:`repoze.bfg.configuration.ConfigurationError` exception.
+
+ If a *module* or *module name* (as opposed to a package or package
+ name) is supplied as ``package``, its containing package is
+ computed and this package used to derive the package name (all
+ names are resolved relative to packages, never to modules). For
+ example, if the ``package`` argument to this type was passed the
+ string ``xml.dom.expatbuilder``, and ``.mindom`` is supplied to
+ the ``resolve`` method, the resulting import would be for
+ ``xml.minidom``, because ``xml.dom.expatbuilder`` is a module
+ object, not a package object.
+
+ If a *package* or *package name* (as opposed to a module or module
+ name) is supplied as ``package``, this package will be used to
+ relative compute dotted names. For example, if the ``package``
+ argument to this type was passed the string ``xml.dom``, and
+ ``.minidom`` is supplied to the ``resolve`` method, the resulting
+ import would be for ``xml.minidom``.
When a dotted name cannot be resolved, a
:class:`repoze.bfg.exceptions.ConfigurationError` error is raised.
"""
def __init__(self, package):
- self.package = package
+ if package is None:
+ self.package_name = None
+ self.package = None
+ else:
+ if isinstance(package, basestring):
+ try:
+ __import__(package)
+ except ImportError:
+ raise ConfigurationError(
+ 'The dotted name %r cannot be imported' % (package,))
+ package = sys.modules[package]
+ self.package = package_of(package)
+ self.package_name = self.package.__name__
def _pkg_resources_style(self, value):
""" package.module:attr style """
- import pkg_resources
if value.startswith('.') or value.startswith(':'):
- if not self.package:
+ if not self.package_name:
raise ConfigurationError(
- 'relative name %r irresolveable without package' % (value,))
+ 'relative name %r irresolveable without '
+ 'package_name' % (value,))
if value in ['.', ':']:
- value = self.package.__name__
+ value = self.package_name
else:
- value = self.package.__name__ + value
+ value = self.package_name + value
return pkg_resources.EntryPoint.parse(
'x=%s' % value).load(False)
def _zope_dottedname_style(self, value):
""" package.module.attr style """
- module = self.package and self.package.__name__ or None
+ module = self.package_name and self.package_name or None
if value == '.':
- if self.package is None:
+ if self.package_name is None:
raise ConfigurationError(
'relative name %r irresolveable without package' % (value,))
name = module.split('.')
@@ -2464,14 +2526,20 @@ class DottedNameResolver(object):
return found
- def __call__(self, dotted):
+ def resolve(self, dotted):
if not isinstance(dotted, basestring):
raise ConfigurationError('%r is not a string' % (dotted,))
- try:
- if ':' in dotted:
- return self._pkg_resources_style(dotted)
- else:
- return self._zope_dottedname_style(dotted)
- except ImportError:
- raise ConfigurationError(
- 'The dotted name %r cannot be imported' % (dotted,))
+ return self.maybe_resolve(dotted)
+
+ def maybe_resolve(self, dotted):
+ if isinstance(dotted, basestring):
+ try:
+ if ':' in dotted:
+ return self._pkg_resources_style(dotted)
+ else:
+ return self._zope_dottedname_style(dotted)
+ except ImportError:
+ raise ConfigurationError(
+ 'The dotted name %r cannot be imported' % (dotted,))
+ return dotted
+
diff --git a/repoze/bfg/path.py b/repoze/bfg/path.py
index bb27e556a..b5850968f 100644
--- a/repoze/bfg/path.py
+++ b/repoze/bfg/path.py
@@ -35,6 +35,12 @@ def package_name(pkg_or_module):
return pkg_name
return pkg_name.rsplit('.', 1)[0]
+def package_of(pkg_or_module):
+ """ Return the package of a module or return the package itself """
+ pkg_name = package_name(pkg_or_module)
+ __import__(pkg_name)
+ return sys.modules[pkg_name]
+
def caller_package(level=2, caller_module=caller_module):
# caller_module in arglist for tests
module = caller_module(level+1)
diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py
index 88c797c0f..f11aee2c1 100644
--- a/repoze/bfg/tests/test_configuration.py
+++ b/repoze/bfg/tests/test_configuration.py
@@ -179,6 +179,37 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'),
renderer)
+ def test_with_package_module(self):
+ from repoze.bfg.tests import test_configuration
+ import repoze.bfg.tests
+ config = self._makeOne()
+ newconfig = config.with_package(test_configuration)
+ self.assertEqual(newconfig.package, repoze.bfg.tests)
+
+ def test_with_package_package(self):
+ import repoze.bfg.tests
+ config = self._makeOne()
+ newconfig = config.with_package(repoze.bfg.tests)
+ self.assertEqual(newconfig.package, repoze.bfg.tests)
+
+ def test_maybe_dotted_string_success(self):
+ import repoze.bfg.tests
+ config = self._makeOne()
+ result = config.maybe_dotted('repoze.bfg.tests')
+ self.assertEqual(result, repoze.bfg.tests)
+
+ def test_maybe_dotted_string_fail(self):
+ from repoze.bfg.configuration import ConfigurationError
+ config = self._makeOne()
+ self.assertRaises(ConfigurationError,
+ config.maybe_dotted, 'cant.be.found')
+
+ def test_maybe_dotted_notstring_success(self):
+ import repoze.bfg.tests
+ config = self._makeOne()
+ result = config.maybe_dotted(repoze.bfg.tests)
+ self.assertEqual(result, repoze.bfg.tests)
+
def test_setup_registry_fixed(self):
class DummyRegistry(object):
def subscribers(self, events, name):
@@ -3604,9 +3635,9 @@ class TestDottedNameResolver(unittest.TestCase):
def test__zope_dottedname_style_resolve_relative_leading_dots(self):
import repoze.bfg.tests.test_configuration
- typ = self._makeOne(package=repoze.bfg.tests.test_configuration)
+ typ = self._makeOne(package=repoze.bfg.tests)
result = typ._zope_dottedname_style(
- '..test_configuration.TestDottedNameResolver')
+ '..tests.test_configuration.TestDottedNameResolver')
self.assertEqual(result, self.__class__)
def test__zope_dottedname_style_resolve_relative_is_dot(self):
@@ -3665,13 +3696,7 @@ class TestDottedNameResolver(unittest.TestCase):
self.assertRaises(ImportError, typ._pkg_resources_style,
'repoze.bfg.tests:nonexisting')
- def test__pkg_resources_style_resolve_relative_startswith_colon(self):
- import repoze.bfg.tests.test_configuration
- typ = self._makeOne(package=repoze.bfg.tests.test_configuration)
- result = typ._pkg_resources_style(':TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_resolve_relative_startswith_dot(self):
+ def test__pkg_resources_style_resolve_relative(self):
import repoze.bfg.tests
typ = self._makeOne(package=repoze.bfg.tests)
result = typ._pkg_resources_style(
@@ -3696,29 +3721,63 @@ class TestDottedNameResolver(unittest.TestCase):
self.assertRaises(ImportError, typ._pkg_resources_style,
':notexisting')
- def test_deserialize_not_a_string(self):
+ def test_resolve_not_a_string(self):
typ = self._makeOne()
- e = self.config_exc(typ, None)
+ e = self.config_exc(typ.resolve, None)
self.assertEqual(e.args[0], 'None is not a string')
- def test_deserialize_using_pkgresources_style(self):
+ def test_resolve_using_pkgresources_style(self):
typ = self._makeOne()
- result = typ(
+ result = typ.resolve(
'repoze.bfg.tests.test_configuration:TestDottedNameResolver')
self.assertEqual(result, self.__class__)
- def test_deserialize_using_zope_dottedname_style(self):
+ def test_resolve_using_zope_dottedname_style(self):
typ = self._makeOne()
- result = typ(
+ result = typ.resolve(
'repoze.bfg.tests.test_configuration:TestDottedNameResolver')
self.assertEqual(result, self.__class__)
- def test_deserialize_style_raises(self):
+ def test_resolve_missing_raises(self):
typ = self._makeOne()
- e = self.config_exc(typ, 'cant.be.found')
+ e = self.config_exc(typ.resolve, 'cant.be.found')
self.assertEqual(e.args[0],
"The dotted name 'cant.be.found' cannot be imported")
+ def test_ctor_string_module_resolveable(self):
+ import repoze.bfg.tests
+ typ = self._makeOne('repoze.bfg.tests.test_configuration')
+ self.assertEqual(typ.package, repoze.bfg.tests)
+ self.assertEqual(typ.package_name, 'repoze.bfg.tests')
+
+ def test_ctor_string_package_resolveable(self):
+ import repoze.bfg.tests
+ typ = self._makeOne('repoze.bfg.tests')
+ self.assertEqual(typ.package, repoze.bfg.tests)
+ self.assertEqual(typ.package_name, 'repoze.bfg.tests')
+
+ def test_ctor_string_irresolveable(self):
+ from repoze.bfg.configuration import ConfigurationError
+ self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found')
+
+ def test_ctor_module(self):
+ import repoze.bfg.tests
+ import repoze.bfg.tests.test_configuration
+ typ = self._makeOne(repoze.bfg.tests.test_configuration)
+ self.assertEqual(typ.package, repoze.bfg.tests)
+ self.assertEqual(typ.package_name, 'repoze.bfg.tests')
+
+ def test_ctor_package(self):
+ import repoze.bfg.tests
+ typ = self._makeOne(repoze.bfg.tests)
+ self.assertEqual(typ.package, repoze.bfg.tests)
+ self.assertEqual(typ.package_name, 'repoze.bfg.tests')
+
+ def test_ctor_None(self):
+ typ = self._makeOne(None)
+ self.assertEqual(typ.package, None)
+ self.assertEqual(typ.package_name, None)
+
class Test_isexception(unittest.TestCase):
def _callFUT(self, ob):
from repoze.bfg.configuration import isexception
diff --git a/repoze/bfg/tests/test_path.py b/repoze/bfg/tests/test_path.py
index dac32b244..8ee0474f9 100644
--- a/repoze/bfg/tests/test_path.py
+++ b/repoze/bfg/tests/test_path.py
@@ -112,6 +112,24 @@ class TestPackagePath(unittest.TestCase):
self.failIf(hasattr(module, '__bfg_abspath__'))
self.assertEqual(result, module.package_path)
+class TestPackageOf(unittest.TestCase):
+ def _callFUT(self, package):
+ from repoze.bfg.path import package_of
+ return package_of(package)
+
+ def test_it_package(self):
+ from repoze.bfg import tests
+ package = DummyPackageOrModule(tests)
+ result = self._callFUT(package)
+ self.assertEqual(result, tests)
+
+ def test_it_module(self):
+ import repoze.bfg.tests.test_configuration
+ from repoze.bfg import tests
+ package = DummyPackageOrModule(repoze.bfg.tests.test_configuration)
+ result = self._callFUT(package)
+ self.assertEqual(result, tests)
+
class TestPackageName(unittest.TestCase):
def _callFUT(self, package):
from repoze.bfg.path import package_name