summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2010-08-13 03:03:03 +0000
committerChris McDonough <chrism@agendaless.com>2010-08-13 03:03:03 +0000
commit39480c04dedfcac3b0255f2030a3e58daae7dd0e (patch)
tree7adb9fc0c6bc1dcbe548183a8518c2f91df15d5a
parent4576408321cbe3510d8cf594f694b20707fbb17f (diff)
downloadpyramid-39480c04dedfcac3b0255f2030a3e58daae7dd0e.tar.gz
pyramid-39480c04dedfcac3b0255f2030a3e58daae7dd0e.tar.bz2
pyramid-39480c04dedfcac3b0255f2030a3e58daae7dd0e.zip
- 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.
-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