diff options
| author | Stephan Richter <stephan.richter@gmail.com> | 2014-02-05 22:18:35 -0500 |
|---|---|---|
| committer | Stephan Richter <stephan.richter@gmail.com> | 2014-02-05 22:20:15 -0500 |
| commit | 5a5625825c7cd05f02b5a19f70408cc66f6ef153 (patch) | |
| tree | a6cc6b2b12515d2b634e6fb3f6c9159f9491c6e3 | |
| parent | 2429314dadbab3261f82fdec372dbf99e77a24db (diff) | |
| download | zope-component-5a5625825c7cd05f02b5a19f70408cc66f6ef153.tar.gz | |
Implemented ability to specify adapter and utility names in Python. Use
the ``@zope.component.named(name)`` decorator to specify the name.
All tox environments pass and coverage is at 100%.
| -rw-r--r-- | CHANGES.rst | 5 | ||||
| -rw-r--r-- | docs/zcml.rst | 43 | ||||
| -rw-r--r-- | setup.py | 4 | ||||
| -rw-r--r-- | src/zope/component/__init__.py | 1 | ||||
| -rw-r--r-- | src/zope/component/_declaration.py | 5 | ||||
| -rw-r--r-- | src/zope/component/testfiles/components.py | 10 | ||||
| -rw-r--r-- | src/zope/component/tests/test_zcml.py | 55 | ||||
| -rw-r--r-- | src/zope/component/zcml.py | 12 |
8 files changed, 113 insertions, 22 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index ac7e022..6cdba47 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ CHANGES ******* -4.1.1 (unreleased) +4.2.0 (unreleased) ================== - Updated ``boostrap.py`` to version 2.2. @@ -9,6 +9,9 @@ CHANGES - Reset the cached ``adapter_hooks`` at ``zope.testing.cleanup.cleanUp`` time (LP1100501). +- Implemented ability to specify adapter and utility names in Python. Use + the ``@zope.component.named(name)`` decorator to specify the name. + 4.1.0 (2013-02-28) ================== diff --git a/docs/zcml.rst b/docs/zcml.rst index 330d353..cb94284 100644 --- a/docs/zcml.rst +++ b/docs/zcml.rst @@ -69,9 +69,9 @@ the <adapter /> directive: .. doctest:: + >>> import zope.component >>> from zope.component.tests.examples import clearZCML >>> clearZCML() - >>> import zope.component >>> zope.component.queryAdapter(Content(), IApp, 'test') is None True @@ -143,13 +143,13 @@ Of course, if no factory is provided at all, we will get an error: ValueError: No factory specified -Declaring ``for`` and ``provides`` in Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Declaring ``for``, ``provides`` and ``name`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The <adapter /> directive can figure out from the in-line Python -declaration (using ``zope.component.adapts()`` or -``zope.component.adapter()`` as well as ``zope.interface.implements``) -what the adapter should be registered for and what it provides: +The <adapter /> directive can figure out from the in-line Python declaration +(using ``zope.component.adapts()`` or ``zope.component.adapter()``, +``zope.interface.implements`` as well as ``zope.component.named``) what the +adapter should be registered for and what it provides: .. doctest:: @@ -193,6 +193,14 @@ ZCML can't figure out what it should provide either: ZopeXMLConfigurationError: File "<string>", line 4.2-7.8 TypeError: Missing 'provides' attribute +Let's now register an adapter that has a name specified in Python: + + >>> runSnippet(''' + ... <adapter factory="zope.component.testfiles.components.Comp4" />''') + + >>> zope.component.getAdapter(Content(), IApp, 'app').__class__ + <class 'zope.component.testfiles.components.Comp4'> + A not so common edge case is registering adapters directly for classes, not for interfaces. For example: @@ -1037,6 +1045,27 @@ We can repeat the same drill for utility factories: ZopeXMLConfigurationError: File "<string>", line 4.2-4.59 TypeError: Missing 'provides' attribute +Declaring ``name`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's now register a utility that has a name specified in Python: + + >>> runSnippet(''' + ... <utility component="zope.component.testfiles.components.comp4" />''') + + >>> from zope.component.testfiles.components import comp4 + >>> zope.component.getUtility(IApp, name='app') is comp4 + True + + >>> runSnippet(''' + ... <utility factory="zope.component.testfiles.components.Comp4" />''') + + >>> zope.component.getUtility(IApp, name='app') is comp4 + False + >>> zope.component.getUtility(IApp, name='app').__class__ + <class 'zope.component.testfiles.components.Comp4'> + + Protected utilities ~~~~~~~~~~~~~~~~~~~ @@ -70,7 +70,7 @@ def read(*rnames): setup( name='zope.component', - version='4.1.1.dev0', + version='4.2.0.dev0', url='http://pypi.python.org/pypi/zope.component', license='ZPL 2.1', description='Zope Component Architecture', @@ -105,7 +105,7 @@ setup( tests_require = TESTS_REQUIRE, test_suite='__main__.alltests', install_requires=['setuptools', - 'zope.interface>=3.8.0', + 'zope.interface>=4.1.0', 'zope.event', ], include_package_data = True, diff --git a/src/zope/component/__init__.py b/src/zope/component/__init__.py index 28e0808..2b13302 100644 --- a/src/zope/component/__init__.py +++ b/src/zope/component/__init__.py @@ -16,6 +16,7 @@ from zope.interface import Interface from zope.interface import implementedBy from zope.interface import moduleProvides +from zope.interface import named from zope.interface import providedBy from zope.component.interfaces import ComponentLookupError diff --git a/src/zope/component/_declaration.py b/src/zope/component/_declaration.py index b434d75..65a62d3 100644 --- a/src/zope/component/_declaration.py +++ b/src/zope/component/_declaration.py @@ -15,7 +15,7 @@ """ import sys -from zope.component._compat import CLASS_TYPES +from zope.component._compat import CLASS_TYPES, _BLANK class adapter(object): @@ -46,6 +46,9 @@ def adapts(*interfaces): def adaptedBy(ob): return getattr(ob, '__component_adapts__', None) +def getName(ob): + return getattr(ob, '__component_name__', _BLANK) + class _adapts_descr(object): def __init__(self, interfaces): self.interfaces = interfaces diff --git a/src/zope/component/testfiles/components.py b/src/zope/component/testfiles/components.py index efbd2fb..d9dd5c2 100644 --- a/src/zope/component/testfiles/components.py +++ b/src/zope/component/testfiles/components.py @@ -17,6 +17,7 @@ from zope.interface import Interface from zope.interface import Attribute from zope.interface import implementer from zope.component import adapter +from zope.component import named class IAppb(Interface): a = Attribute('test attribute') @@ -40,7 +41,6 @@ class Content(object): @adapter(IContent) @implementer(IApp) class Comp(object): - pass def __init__(self, *args): # Ignore arguments passed to constructor @@ -57,6 +57,14 @@ class Comp3(object): def __init__(self, context): self.context = context +@adapter(IContent) +@implementer(IApp) +@named('app') +class Comp4(object): + def __init__(self, context=None): + self.context = context + comp = Comp() +comp4 = Comp4() content = Content() diff --git a/src/zope/component/tests/test_zcml.py b/src/zope/component/tests/test_zcml.py index f64e822..22cd56a 100644 --- a/src/zope/component/tests/test_zcml.py +++ b/src/zope/component/tests/test_zcml.py @@ -83,7 +83,7 @@ class Test_adapter(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.component.zcml import adapter return adapter(*args, **kw) - + def test_empty_factory(self): from zope.interface import Interface from zope.component.zcml import ComponentConfigurationError @@ -92,7 +92,7 @@ class Test_adapter(unittest.TestCase): _cfg_ctx = _makeConfigContext() self.assertRaises(ComponentConfigurationError, self._callFUT, _cfg_ctx, [], [Interface], IFoo) - + def test_multiple_factory_multiple_for_(self): from zope.interface import Interface from zope.component.zcml import ComponentConfigurationError @@ -116,7 +116,27 @@ class Test_adapter(unittest.TestCase): self.context = context _cfg_ctx = _makeConfigContext() self.assertRaises(TypeError, self._callFUT, _cfg_ctx, [_Factory]) - + + def test_no_name(self): + from zope.interface import Interface + class IFoo(Interface): + pass + class IBar(Interface): + pass + from zope.component import adapter, named + from zope.interface import implementer + @adapter(IFoo) + @implementer(IBar) + @named('bar') + class _Factory(object): + def __init__(self, context): + self.context = context + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, [_Factory]) + # Register the adapter + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['args'][4], 'bar') + def test_no_for__factory_adapts_no_provides_factory_not_implements(self): from zope.interface import Interface from zope.component._declaration import adapter @@ -126,7 +146,7 @@ class Test_adapter(unittest.TestCase): self.context = context _cfg_ctx = _makeConfigContext() self.assertRaises(TypeError, self._callFUT, _cfg_ctx, [_Factory]) - + def test_multiple_factory_single_for__w_name(self): from zope.interface import Interface from zope.component.interface import provideInterface @@ -164,7 +184,7 @@ class Test_adapter(unittest.TestCase): self.assertEqual(action['callable'], provideInterface) self.assertEqual(action['discriminator'], None) self.assertEqual(action['args'], ('', Interface)) - + @skipIfNoSecurity def test_single_factory_single_for_w_permission(self): from zope.interface import Interface @@ -194,7 +214,7 @@ class Test_adapter(unittest.TestCase): self.assertEqual(action['args'][3], IFoo) self.assertEqual(action['args'][4], '') self.assertEqual(action['args'][5], 'TESTING') - + @skipIfNoSecurity def test_single_factory_single_for_w_locate_no_permission(self): from zope.interface import Interface @@ -223,7 +243,7 @@ class Test_adapter(unittest.TestCase): self.assertEqual(action['args'][3], IFoo) self.assertEqual(action['args'][4], '') self.assertEqual(action['args'][5], 'TESTING') - + @skipIfNoSecurity def test_single_factory_single_for_w_trusted_no_permission(self): from zope.interface import Interface @@ -251,7 +271,7 @@ class Test_adapter(unittest.TestCase): self.assertEqual(action['args'][3], IFoo) self.assertEqual(action['args'][4], '') self.assertEqual(action['args'][5], 'TESTING') - + def test_no_for__no_provides_factory_adapts_factory_implements(self): from zope.interface import Interface from zope.interface import implementer @@ -610,7 +630,7 @@ class Test_utility(unittest.TestCase): self.assertEqual(action['discriminator'], None) self.assertEqual(action['args'], ('', IFoo)) - def test_w_component_w_provides_w_naem(self): + def test_w_component_w_provides_w_name(self): from zope.interface import Interface from zope.component.interface import provideInterface from zope.component.zcml import handler @@ -638,6 +658,23 @@ class Test_utility(unittest.TestCase): self.assertEqual(action['discriminator'], None) self.assertEqual(action['args'], ('', IFoo)) + def test_w_component_wo_provides_wo_name(self): + from zope.interface import Interface, implementer, named + from zope.component.zcml import handler + class IFoo(Interface): + pass + @implementer(IFoo) + @named('foo') + class Foo(object): + pass + foo = Foo() + _cfg_ctx = _makeConfigContext() + self._callFUT(_cfg_ctx, component=foo) + action =_cfg_ctx._actions[0][1] + self.assertEqual(action['args'][1], foo) + self.assertEqual(action['args'][2], IFoo) + self.assertEqual(action['args'][3], 'foo') + def test_w_component_wo_provides_component_provides(self): from zope.interface import Interface from zope.interface import directlyProvides diff --git a/src/zope/component/zcml.py b/src/zope/component/zcml.py index d760ce1..37a1f32 100644 --- a/src/zope/component/zcml.py +++ b/src/zope/component/zcml.py @@ -26,7 +26,7 @@ from zope.interface import providedBy from zope.schema import TextLine from zope.component._api import getSiteManager -from zope.component._declaration import adaptedBy +from zope.component._declaration import adaptedBy, getName from zope.component.interface import provideInterface from zope.component._compat import _BLANK @@ -182,6 +182,10 @@ def adapter(_context, factory, provides=None, for_=None, permission=None, if provides is None: raise TypeError("Missing 'provides' attribute") + if name == '': + if len(factory) == 1: + name = getName(factory[0]) + # Generate a single factory from multiple factories: factories = factory if len(factories) == 1: @@ -380,6 +384,12 @@ def utility(_context, provides=None, component=None, factory=None, else: raise TypeError("Missing 'provides' attribute") + if name == '': + if factory: + name = getName(factory) + else: + name = getName(component) + if permission is not None: component = proxify(component, provides=provides, permission=permission) |
