diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-12-19 21:14:30 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-12-19 21:14:30 +0000 |
| commit | 251ce7d166fa901b33f20cf46315ec464e3eac64 (patch) | |
| tree | 2cd31c1de76f3a915dfb702cc8c18edff27e60b1 /repoze | |
| parent | 12daf9e069b66aef5715a14641fc269367c091dd (diff) | |
| download | pyramid-251ce7d166fa901b33f20cf46315ec464e3eac64.tar.gz pyramid-251ce7d166fa901b33f20cf46315ec464e3eac64.tar.bz2 pyramid-251ce7d166fa901b33f20cf46315ec464e3eac64.zip | |
- Add two new APIs to the ``repoze.bfg.configuration.Configurator``
class: ``add_adapter`` and ``add_utility``. These, respectively,
perform the same functions as the ``registerAdapter`` and
``registerUtility`` functions of a ZCA registry. They were added to
allow for a more consistent testing API for applications that make
use of the ZCA directly.
- Cause the ``adapter``, ``utility``, and ``subscriber`` ZCML
directives to use a ``Configurator`` instance and associated
configurator APIs rather than a ZCA registry directly.
Diffstat (limited to 'repoze')
| -rw-r--r-- | repoze/bfg/configuration.py | 50 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_configuration.py | 40 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 80 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 230 |
4 files changed, 226 insertions, 174 deletions
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py index 0ee08868d..7b204fec3 100644 --- a/repoze/bfg/configuration.py +++ b/repoze/bfg/configuration.py @@ -10,6 +10,7 @@ from zope.configuration import xmlconfig from zope.interface import Interface from zope.interface import implementedBy +from zope.interface import providedBy from zope.interface.interfaces import IInterface from zope.interface import implements @@ -291,7 +292,7 @@ class Configurator(object): """ return self.manager.pop() - def add_subscriber(self, subscriber, iface=None): + 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 @@ -305,9 +306,54 @@ class Configurator(object): iface = (Interface,) if not isinstance(iface, (tuple, list)): iface = (iface,) - self.registry.registerHandler(subscriber, iface) + self.registry.registerHandler(subscriber, iface, info=info) return subscriber + def add_subscription_adapter(self, factory, required=None, provided=None, + info=u''): + """Add a Zope Component Architecture subscription adapter. + What is a subscription adapter, you ask? I have no idea + either. This is currently only here to support the + ``subscriber`` ZCML directive. This is not a published API + until I figure out why I would need a subscription adapter.""" + self.registry.registerSubscriptionAdapter(factory, required=required, + provided=provided, info=info) + + def add_adapter(self, factory, required=None, provided=None, name='', + info=u''): + """Add a :term:`Zope Component Architecture` adapter. Use of + this method is the equivalent of using an ``adapter`` + :term:`ZCML declaration` or the ``registerAdapter`` method of + a ZCA registry. + + .. note:: This method is not useful unless you use :term:`Zope + Component Architecture` APIs in your :mod:`repoze.bfg` + application directly. + """ + self.registry.registerAdapter(factory, required=required, + provided=provided, name=name, info=info) + + def add_utility(self, component=None, provided=None, name=u'', info=u'', + factory=None): + """Add a :term:`Zope Component Architecture` utility. Use of + this method is the equivalent of using a ``utility`` + :term:`ZCML declaration` or the ``registerUtility`` method of + a ZCA registry. + + .. note:: This method is not useful unless you use :term:`Zope + Component Architecture` APIs in your :mod:`repoze.bfg` + application directly. + """ + if factory: + kw = dict(factory=factory) + else: + # older component registries don't accept factory as a kwarg, + # so if we don't need it, we don't pass it + kw = {} + self.registry.registerUtility( + component=component, provided=provided, name=name, info=info, + **kw) + def make_wsgi_app(self): """ Returns a :mod:`repoze.bfg` WSGI application representing the current configuration state and sends a diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py index 7d714f42a..520f9a5bc 100644 --- a/repoze/bfg/tests/test_configuration.py +++ b/repoze/bfg/tests/test_configuration.py @@ -1813,6 +1813,37 @@ class ConfiguratorTests(unittest.TestCase): result = render_view_to_response(ctx, req, 'pod_notinit') self.assertEqual(result, None) + def test_add_subscription_adapter(self): + config = self._makeOne() + def factory(abc): pass + config.add_subscription_adapter(factory, (IDummy,), IDummy, + info='info') + adapters = config.registry.registeredSubscriptionAdapters() + self.assertEqual(list(adapters)[0].factory, factory) + + def test_add_adapter(self): + config = self._makeOne() + def factory(abc): return 'OK' + config.add_adapter(factory, (IDummy,), IDummy, name='foo', + info='info') + result = config.registry.adapters.lookup((IDummy,), IDummy, name='foo') + self.assertEqual(result(None), 'OK') + + def test_add_utility_no_factory(self): + config = self._makeOne() + def component(): pass + config.add_utility(component, IDummy, name='foo', info='info') + result = config.registry.queryUtility(IDummy, name='foo') + self.assertEqual(result, component) + + def test_add_utility_with_factory(self): + config = self._makeOne() + def factory(): return 'OK' + config.add_utility(None, IDummy, name='foo', info='info', + factory=factory) + result = config.registry.queryUtility(IDummy, name='foo') + self.assertEqual(result, 'OK') + class Test__map_view(unittest.TestCase): def setUp(self): from repoze.bfg.registry import Registry @@ -2812,3 +2843,12 @@ class DummyThreadLocalManager(object): self.pushed = d def pop(self): self.popped = True + +class IFactory(Interface): + pass + +class DummyFactory(object): + implements(IFactory) + def __call__(self): + """ """ + diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index cbbbe664c..59254846f 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -724,7 +724,7 @@ class TestAdapterDirective(unittest.TestCase): provides=None, for_=None) def test_for_is_None_adaptedBy_set(self): - from repoze.bfg.zcml import handler + from repoze.bfg.configuration import Configurator context = DummyContext() factory = DummyFactory() factory.__component_adapts__ = (IDummy,) @@ -733,11 +733,10 @@ class TestAdapterDirective(unittest.TestCase): regadapt = context.actions[0] self.assertEqual(regadapt['discriminator'], ('adapter', (IDummy,), IFactory, '')) - self.assertEqual(regadapt['callable'], - handler) + self.assertEqual(regadapt['callable'].im_func, + Configurator.add_adapter.im_func) self.assertEqual(regadapt['args'], - ('registerAdapter', factory, (IDummy,), IFactory, - '', None)) + (factory, (IDummy,), IFactory, '', None)) def test_provides_missing(self): context = DummyContext() @@ -746,17 +745,16 @@ class TestAdapterDirective(unittest.TestCase): provides=None, for_=(IDummy,)) def test_provides_obtained_via_implementedBy(self): - from repoze.bfg.zcml import handler + from repoze.bfg.configuration import Configurator context = DummyContext() self._callFUT(context, [DummyFactory], for_=(IDummy,)) regadapt = context.actions[0] self.assertEqual(regadapt['discriminator'], ('adapter', (IDummy,), IFactory, '')) - self.assertEqual(regadapt['callable'], - handler) + self.assertEqual(regadapt['callable'].im_func, + Configurator.add_adapter.im_func) self.assertEqual(regadapt['args'], - ('registerAdapter', DummyFactory, (IDummy,), IFactory, - '', None)) + (DummyFactory, (IDummy,), IFactory, '', None)) def test_multiple_factories_multiple_for(self): context = DummyContext() @@ -774,7 +772,7 @@ class TestAdapterDirective(unittest.TestCase): for_=(IDummy, IDummy)) def test_rolled_up_factories(self): - from repoze.bfg.zcml import handler + from repoze.bfg.configuration import Configurator context = DummyContext() factory = DummyFactory() self._callFUT(context, @@ -784,10 +782,9 @@ class TestAdapterDirective(unittest.TestCase): regadapt = context.actions[0] self.assertEqual(regadapt['discriminator'], ('adapter', (IDummy,), IFactory, '')) - self.assertEqual(regadapt['callable'], - handler) - self.assertEqual(len(regadapt['args']), 6) - + self.assertEqual(regadapt['callable'].im_func, + Configurator.add_adapter.im_func) + self.assertEqual(regadapt['args'][0].__module__, 'repoze.bfg.zcml') class TestSubscriberDirective(unittest.TestCase): def setUp(self): @@ -833,7 +830,7 @@ class TestSubscriberDirective(unittest.TestCase): factory=factory, handler=None, provides=IFactory) def test_register_with_factory(self): - from repoze.bfg.zcml import handler + from repoze.bfg.configuration import Configurator context = DummyContext() factory = DummyFactory() self._callFUT(context, for_=(IDummy,), @@ -841,13 +838,13 @@ class TestSubscriberDirective(unittest.TestCase): self.assertEqual(len(context.actions), 1) subadapt = context.actions[0] self.assertEqual(subadapt['discriminator'], None) - self.assertEqual(subadapt['callable'], handler) + self.assertEqual(subadapt['callable'].im_func, + Configurator.add_subscription_adapter.im_func) self.assertEqual(subadapt['args'], - ('registerSubscriptionAdapter', factory, - (IDummy,), IFactory, u'', None) ) + (factory, (IDummy,), IFactory, None) ) def test_register_with_handler(self): - from repoze.bfg.zcml import handler + from repoze.bfg.configuration import Configurator context = DummyContext() factory = DummyFactory() self._callFUT(context, for_=(IDummy,), @@ -855,10 +852,9 @@ class TestSubscriberDirective(unittest.TestCase): self.assertEqual(len(context.actions), 1) subadapt = context.actions[0] self.assertEqual(subadapt['discriminator'], None) - self.assertEqual(subadapt['callable'], handler) - self.assertEqual(subadapt['args'], - ('registerHandler', factory, - (IDummy,), u'', None) ) + self.assertEqual(subadapt['callable'].im_func, + Configurator.add_subscriber.im_func) + self.assertEqual(subadapt['args'], (factory, (IDummy,), None) ) class TestUtilityDirective(unittest.TestCase): def setUp(self): @@ -881,30 +877,29 @@ class TestUtilityDirective(unittest.TestCase): self.assertRaises(TypeError, self._callFUT, context, provides=None) def test_provides_from_factory_implements(self): - from repoze.bfg.zcml import handler + from repoze.bfg.configuration import Configurator context = DummyContext() self._callFUT(context, factory=DummyFactory) self.assertEqual(len(context.actions), 1) utility = context.actions[0] self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) - self.assertEqual(utility['callable'], handler) + self.assertEqual(utility['callable'].im_func, + Configurator.add_utility.im_func) self.assertEqual(utility['args'], - ('registerUtility', None, IFactory, '')) - self.assertEqual(utility['kw'], - {'factory': DummyFactory}) + (None, IFactory, '', None, DummyFactory)) def test_provides_from_component_provides(self): - from repoze.bfg.zcml import handler + from repoze.bfg.configuration import Configurator context = DummyContext() component = DummyFactory() self._callFUT(context, component=component) self.assertEqual(len(context.actions), 1) utility = context.actions[0] self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) - self.assertEqual(utility['callable'], handler) + self.assertEqual(utility['callable'].im_func, + Configurator.add_utility.im_func) self.assertEqual(utility['args'], - ('registerUtility', component, IFactory, '')) - self.assertEqual(utility['kw'], {}) + (component, IFactory, '', None, None)) class TestLoadZCML(unittest.TestCase): def setUp(self): @@ -918,25 +913,6 @@ class TestLoadZCML(unittest.TestCase): import repoze.bfg.includes xmlconfig.file('configure.zcml', package=repoze.bfg.includes) -class TestHandler(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, methodName, *arg, **kw): - from repoze.bfg.zcml import handler - return handler(methodName, *arg, **kw) - - def test_it(self): - def foo(): - """ """ - from zope.interface import Interface - class IWhatever(Interface): - pass - self._callFUT('registerUtility', foo, IWhatever) - class TestRolledUpFactory(unittest.TestCase): def _callFUT(self, *factories): from repoze.bfg.zcml import _rolledUpFactory diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index 7ac32fae2..90f863fb3 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -543,35 +543,50 @@ def scan(_context, package): args=(package, _context.info) ) -def zcml_configure(name, package): - """ Given a ZCML filename as ``name`` and a Python package as - ``package`` which the filename should be relative to, load the - ZCML into the current ZCML registry. - - .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. +class IAdapterDirective(Interface): + """ + Register an adapter """ - context = ConfigurationMachine() - xmlconfig.registerCommonDirectives(context) - context.package = package - xmlconfig.include(context, name, package) - context.execute_actions(clear=False) # the raison d'etre - return context.actions -file_configure = zcml_configure # backwards compat (>0.8.1) + factory = Tokens( + title=u"Adapter factory/factories", + description=(u"A list of factories (usually just one) that create" + " the adapter instance."), + required=True, + value_type=GlobalObject() + ) -def handler(methodName, *args, **kwargs): - registry = get_current_registry() - method = getattr(registry, methodName) - method(*args, **kwargs) + provides = GlobalInterface( + title=u"Interface the component provides", + description=(u"This attribute specifies the interface the adapter" + " instance must provide."), + required=False, + ) -def adapter(_context, factory, provides=None, for_=None, name=''): + for_ = Tokens( + title=u"Specifications to be adapted", + description=u"This should be a list of interfaces or classes", + required=False, + value_type=GlobalObject( + missing_value=object(), + ), + ) + + name = TextLine( + title=u"Name", + description=(u"Adapters can have names.\n\n" + "This attribute allows you to specify the name for" + " this adapter."), + required=False, + ) +def adapter(_context, factory, provides=None, for_=None, name=''): if for_ is None: if len(factory) == 1: for_ = getattr(factory[0], '__component_adapts__', None) if for_ is None: - raise TypeError("No for attribute was provided and can't " + raise TypeError("No for argument was provided and can't " "determine what the factory adapts.") for_ = tuple(for_) @@ -583,7 +598,7 @@ def adapter(_context, factory, provides=None, for_=None, name=''): provides = p[0] if provides is None: - raise TypeError("Missing 'provides' attribute") + raise TypeError("Missing 'provided' argument") # Generate a single factory from multiple factories: factories = factory @@ -592,31 +607,37 @@ def adapter(_context, factory, provides=None, for_=None, name=''): elif len(factories) < 1: raise ValueError("No factory specified") elif len(factories) > 1 and len(for_) != 1: - raise ValueError("Can't use multiple factories and multiple for") + raise ValueError("Can't use multiple factories and multiple " + "for") else: factory = _rolledUpFactory(factories) - + + reg = get_current_registry() + config = Configurator(reg, package=_context.package) _context.action( discriminator = ('adapter', for_, provides, name), - callable = handler, - args = ('registerAdapter', - factory, for_, provides, name, _context.info), + callable = config.add_adapter, + args = (factory, for_, provides, name, _context.info), ) -class IAdapterDirective(Interface): +class ISubscriberDirective(Interface): """ - Register an adapter + Register a subscriber """ - factory = Tokens( - title=u"Adapter factory/factories", - description=(u"A list of factories (usually just one) that create" - " the adapter instance."), - required=True, - value_type=GlobalObject() + factory = GlobalObject( + title=u"Subscriber factory", + description=u"A factory used to create the subscriber instance.", + required=False, ) - provides = GlobalInterface( + handler = GlobalObject( + title=u"Handler", + description=u"A callable object that handles events.", + required=False, + ) + + provides = GlobalInterface( title=u"Interface the component provides", description=(u"This attribute specifies the interface the adapter" " instance must provide."), @@ -624,23 +645,14 @@ class IAdapterDirective(Interface): ) for_ = Tokens( - title=u"Specifications to be adapted", + title=u"Interfaces or classes that this subscriber depends on", description=u"This should be a list of interfaces or classes", required=False, value_type=GlobalObject( - missing_value=object(), + missing_value = object(), ), ) - name = TextLine( - title=u"Name", - description=(u"Adapters can have names.\n\n" - "This attribute allows you to specify the name for" - " this adapter."), - required=False, - ) - -_handler = handler def subscriber(_context, for_=None, factory=None, handler=None, provides=None): if factory is None: if handler is None: @@ -664,82 +676,22 @@ def subscriber(_context, for_=None, factory=None, handler=None, provides=None): for_ = tuple(for_) + reg = get_current_registry() + config = Configurator(reg, _context.package) + if handler is not None: _context.action( discriminator = None, - callable = _handler, - args = ('registerHandler', - handler, for_, u'', _context.info), + callable = config.add_subscriber, + args = (handler, for_, _context.info), ) else: _context.action( discriminator = None, - callable = _handler, - args = ('registerSubscriptionAdapter', - factory, for_, provides, u'', _context.info), + callable = config.add_subscription_adapter, + args = (factory, for_, provides, _context.info), ) -class ISubscriberDirective(Interface): - """ - Register a subscriber - """ - - factory = GlobalObject( - title=u"Subscriber factory", - description=u"A factory used to create the subscriber instance.", - required=False, - ) - - handler = GlobalObject( - title=u"Handler", - description=u"A callable object that handles events.", - required=False, - ) - - provides = GlobalInterface( - title=u"Interface the component provides", - description=(u"This attribute specifies the interface the adapter" - " instance must provide."), - required=False, - ) - - for_ = Tokens( - title=u"Interfaces or classes that this subscriber depends on", - description=u"This should be a list of interfaces or classes", - required=False, - value_type=GlobalObject( - missing_value = object(), - ), - ) - -def utility(_context, provides=None, component=None, factory=None, name=''): - if factory and component: - raise TypeError("Can't specify factory and component.") - - if provides is None: - if factory: - provides = list(implementedBy(factory)) - else: - provides = list(providedBy(component)) - if len(provides) == 1: - provides = provides[0] - else: - raise TypeError("Missing 'provides' attribute") - - if factory: - kw = dict(factory=factory) - else: - # older component registries don't accept factory as a kwarg, - # so if we don't need it, we don't pass it - kw = {} - - _context.action( - discriminator = ('utility', provides, name), - callable = handler, - args = ('registerUtility', component, provides, name), - kw = kw, - ) - class IUtilityDirective(Interface): """Register a utility.""" @@ -775,14 +727,27 @@ class IUtilityDirective(Interface): required=False, ) -def _rolledUpFactory(factories): - def factory(ob): - for f in factories: - ob = f(ob) - return ob - # Store the original factory for documentation - factory.factory = factories[0] - return factory +def utility(_context, provides=None, component=None, factory=None, name=''): + if factory and component: + raise TypeError("Can't specify factory and component.") + + if provides is None: + if factory: + provides = list(implementedBy(factory)) + else: + provides = list(providedBy(component)) + if len(provides) == 1: + provides = provides[0] + else: + raise TypeError("Missing 'provides' attribute") + + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + _context.action( + discriminator = ('utility', provides, name), + callable = config.add_utility, + args = (component, provides, name, _context.info, factory), + ) def path_spec(context, path): # Convert an absolute path to a resource in a package to a @@ -805,3 +770,28 @@ def path_spec(context, path): relpath.replace(os.path.sep, '/')) return abspath +def zcml_configure(name, package): + """ Given a ZCML filename as ``name`` and a Python package as + ``package`` which the filename should be relative to, load the + ZCML into the current ZCML registry. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + """ + context = ConfigurationMachine() + xmlconfig.registerCommonDirectives(context) + context.package = package + xmlconfig.include(context, name, package) + context.execute_actions(clear=False) # the raison d'etre + return context.actions + +file_configure = zcml_configure # backwards compat (>0.8.1) + +def _rolledUpFactory(factories): + def factory(ob): + for f in factories: + ob = f(ob) + return ob + # Store the original factory for documentation + factory.factory = factories[0] + return factory + |
