[Zope3-checkins] SVN: Zope3/branches/jim-adapter/src/zope/ Moved
tests for elementary CA directives to zope.component/zcml.txt (also
Philipp von Weitershausen
philikon at philikon.de
Wed Apr 12 14:27:11 EDT 2006
Log message for revision 66900:
Moved tests for elementary CA directives to zope.component/zcml.txt (also
converted them to doctests and sanitized a whole bunch of them that were stupid
for oen reason or another)
Changed:
U Zope3/branches/jim-adapter/src/zope/app/component/meta.zcml
U Zope3/branches/jim-adapter/src/zope/app/component/tests/test_directives.py
A Zope3/branches/jim-adapter/src/zope/component/meta.zcml
U Zope3/branches/jim-adapter/src/zope/component/tests.py
A Zope3/branches/jim-adapter/src/zope/component/zcml.txt
-=-
Modified: Zope3/branches/jim-adapter/src/zope/app/component/meta.zcml
===================================================================
--- Zope3/branches/jim-adapter/src/zope/app/component/meta.zcml 2006-04-12 18:09:30 UTC (rev 66899)
+++ Zope3/branches/jim-adapter/src/zope/app/component/meta.zcml 2006-04-12 18:27:10 UTC (rev 66900)
@@ -2,33 +2,10 @@
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
+ <include package="zope.component" file="meta.zcml" />
+
<meta:directives namespace="http://namespaces.zope.org/zope">
- <meta:directive
- name="interface"
- schema="zope.component.zcml.IInterfaceDirective"
- handler="zope.component.zcml.interface"
- />
-
- <meta:directive
- name="adapter"
- schema="zope.component.zcml.IAdapterDirective"
- handler="zope.component.zcml.adapter"
- />
-
- <meta:directive
- name="subscriber"
- schema="zope.component.zcml.ISubscriberDirective"
- handler="zope.component.zcml.subscriber"
- />
-
- <meta:directive
- name="utility"
- schema="zope.component.zcml.IUtilityDirective"
- handler="zope.component.zcml.utility"
- />
-
-
<!-- BBB 2006/02/24, to be removed after 12 months -->
<meta:directive
name="factory"
Modified: Zope3/branches/jim-adapter/src/zope/app/component/tests/test_directives.py
===================================================================
--- Zope3/branches/jim-adapter/src/zope/app/component/tests/test_directives.py 2006-04-12 18:09:30 UTC (rev 66899)
+++ Zope3/branches/jim-adapter/src/zope/app/component/tests/test_directives.py 2006-04-12 18:27:10 UTC (rev 66900)
@@ -28,7 +28,6 @@
from zope.component.registry import SubscriptionRegistration
from zope.component.interfaces import ComponentLookupError
from zope.component.interface import queryInterface
-from zope.component.zcml import interface
from zope.configuration.xmlconfig import xmlconfig, XMLConfig
from zope.configuration.exceptions import ConfigurationError
@@ -66,27 +65,6 @@
return (''.join(atre.split(r))).strip()
-def testInterface():
- """
- >>> context = Context()
- >>> class I(Interface):
- ... pass
- >>> IContentType.providedBy(I)
- False
- >>> interface(context, I, IContentType)
- >>> context
- ((None,
- <function provideInterface>,
- ('',
- <InterfaceClass zope.app.component.tests.test_directives.I>,
- <InterfaceClass zope.app.content.interfaces.IContentType>)),)
- >>> from zope.interface.interfaces import IInterface
- >>> IContentType.extends(IInterface)
- True
- >>> IInterface.providedBy(I)
- True
- """
-
template = """<configure
xmlns='http://namespaces.zope.org/zope'
xmlns:test='http://www.zope.org/NS/Zope3/test'
@@ -108,861 +86,6 @@
XMLConfig('meta.zcml', zope.app.component)()
XMLConfig('meta.zcml', zope.app.security)()
- def testSubscriber(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- />
- '''
- )))
-
- content = Content()
- a1 = A1()
- subscribers = zapi.subscribers((content, a1), IS)
-
- a3 = subscribers[0]
-
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(a3.context, (content, a1))
-
-
- def testSubscriberDocumentation(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- />
- '''
- )))
-
- gsm = zapi.getGlobalSiteManager()
- doc = [reg.info
- for reg in gsm.registrations()
- if (isinstance(reg, SubscriptionRegistration) and
- reg.provided is IS)][0]
-
- self.assertEqual(`doc`, 'File "<string>", line 6.12-11.16')
-
- def testSubscriberWithPermission(self):
- xmlconfig(StringIO(template % (
- '''
- <permission
- id="y.x"
- title="XY"
- description="Allow XY." />
-
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- permission="y.x"
- />
- '''
- )))
-
- content = Content()
- a1 = A1()
- subscribers = zapi.subscribers((content, a1), IS)
-
- a3 = subscribers[0]
-
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(a3.context, (content, a1))
- self.assertEqual(type(a3).__name__, 'LocationProxy')
-
- def testSubscriber_wo_for(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A3"
- />
- '''
- )))
-
- content = Content()
- a1 = A1()
- a2 = A2()
- subscribers = zapi.subscribers((content, a1, a2), IS)
-
- a3 = subscribers[0]
-
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(a3.context, (content, a1, a2))
-
- def testHandlerSubscriber_no_for(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- handler="zope.app.component.tests.adapter.A3"
- />
- '''
- )))
-
- sm = zapi.getSiteManager()
- a3 = sm.adapters.subscriptions((IContent, I1, I2), None)[0]
- self.assertEqual(a3, A3)
-
-
- def testTrustedSubscriber(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- trusted="yes"
- />
- '''
- )))
- # With an unproxied object, business as usual
- content = Content()
- a1 = A1()
- subscribers = zapi.subscribers((content, a1), IS)
-
- a3 = subscribers[0]
-
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(type(a3).__name__, 'A3')
- self.assertEqual(a3.context, (content, a1))
-
- # Now with a proxied object:
- from zope.security.checker import ProxyFactory
- p = ProxyFactory(content)
-
- # we get a proxied subscriber:
- a3 = zapi.subscribers((p, a1), IS)[0]
- from zope.security.proxy import Proxy
- self.assertEqual(type(a3), Proxy)
-
-
- # behind the security proxy is no locatin proxy:
- from zope.security.proxy import removeSecurityProxy
- self.assert_(removeSecurityProxy(a3).context[0] is content)
- self.assertEqual(type(removeSecurityProxy(a3)).__name__, 'A3')
-
-
- def testLocatableTrustedSubscriber(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- trusted="yes"
- locate="yes"
- />
- '''
- )))
- # With an unproxied object, business as usual
- content = Content()
- a1 = A1()
- subscribers = zapi.subscribers((content, a1), IS)
-
- a3 = subscribers[0]
-
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(type(a3).__name__, 'A3')
- self.assertEqual(a3.context, (content, a1))
-
- # Now with a proxied object:
- from zope.security.checker import ProxyFactory
- p = ProxyFactory(content)
-
- # we get a proxied subscriber:
- a3 = zapi.subscribers((p, a1), IS)[0]
- from zope.security.proxy import Proxy
- self.assertEqual(type(a3), Proxy)
-
- # behind the security proxy is a locatio proxy:
- from zope.security.proxy import removeSecurityProxy
- self.assert_(removeSecurityProxy(a3).context[0] is content)
- self.assertEqual(type(removeSecurityProxy(a3)).__name__,
- 'LocationProxy')
-
- def testSubscriber_w_no_provides(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- handler="zope.app.component.tests.adapter.Handler"
- />
- '''
- )))
-
- content = Content()
- a1 = A1()
- list(zapi.subscribers((content, a1), None))
-
- self.assertEqual(content.args, ((a1,),))
-
- def testSubscriberHavingARequiredClass(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- for="zope.app.component.tests.components.Content"
- provides="zope.app.component.tests.adapter.I1"
- factory="zope.app.component.tests.adapter.A1"
- />
- '''
- )))
-
- subs = zapi.subscribers((Content(),), I1)
- self.assert_(isinstance(subs[0], A1))
-
- class MyContent:
- implements(IContent)
-
- self.assertEqual(zapi.subscribers((MyContent(),), I1), [])
-
- def testMultiSubscriber(self):
- xmlconfig(StringIO(template % (
- '''
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- />
- <subscriber
- provides="zope.app.component.tests.adapter.IS"
- factory="zope.app.component.tests.adapter.A2"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1"
- />
- '''
- )))
-
- content = Content()
- a1 = A1()
- subscribers = zapi.subscribers((content, a1), IS)
-
- expectedLength = 2
- self.assertEqual(len(subscribers), expectedLength)
- classesNotFound = [A2, A3]
- for a in subscribers:
- classesNotFound.remove(a.__class__)
- self.failIf(classesNotFound)
-
- def testAdapter(self):
- # Full import is critical!
- self.assertEqual(IV(Content(), None), None)
-
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.components.Comp"
- provides="zope.app.component.tests.components.IApp"
- for="zope.app.component.tests.components.IContent"
- />
- '''
- )))
-
- self.assertEqual(IApp(Content()).__class__, Comp)
-
- def testAdapterWithPermission(self):
- # Full import is critical!
- self.assertEqual(IV(Content(), None), None)
-
- xmlconfig(StringIO(template % (
- '''
- <permission
- id="y.x"
- title="XY"
- description="Allow XY." />
-
- <adapter
- factory="zope.app.component.tests.components.Comp"
- provides="zope.app.component.tests.components.IApp"
- for="zope.app.component.tests.components.IContent"
- permission="y.x"
- />
- '''
- )))
-
- self.assertEqual(IApp(Content()).__class__, Comp)
- self.assertEqual(type(IApp(Content())).__name__, 'LocationProxy')
-
- def testAdapter_wo_provides_or_for(self):
- # Full import is critical!
- self.assertEqual(IV(Content(), None), None)
-
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.components.Comp"
- />
- '''
- )))
-
- self.assertEqual(IApp(Content()).__class__, Comp)
-
- def testAdapter_wo_provides_and_no_implented_fails(self):
- try:
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A4"
- for="zope.app.component.tests.components.IContent"
- />
- '''
- )))
- except ConfigurationError, v:
- self.assert_("Missing 'provides' attribute" in str(v))
-
- def testAdapter_wo_provides_and_too_many_implented_fails(self):
- try:
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A4"
- for="zope.app.component.tests.components.IContent"
- />
- '''
- )))
- except ConfigurationError, v:
- self.assert_("Missing 'provides' attribute" in str(v))
-
- def testTrustedAdapter(self):
- # Full import is critical!
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A1"
- provides="zope.app.component.tests.adapter.I1"
- for="zope.app.component.tests.components.IContent"
- trusted="yes"
- />
- '''
- )))
-
- # With an unproxied object, business as usual
- ob = Content()
- self.assertEqual(type(I1(ob)).__name__, 'A1')
-
- # Now with a proxied object:
- from zope.security.checker import ProxyFactory
- p = ProxyFactory(ob)
-
- # we get a proxied adapter:
- a = I1(p)
- from zope.security.proxy import Proxy
- self.assertEqual(type(a), Proxy)
-
- # around an unproxied object:
- from zope.security.proxy import removeSecurityProxy
- a = removeSecurityProxy(a)
- self.assertEqual(type(a).__name__, 'A1')
- self.assert_(a.context[0] is ob)
-
-
- def testTrustedAdapterWithPermission(self):
- # Full import is critical!
- xmlconfig(StringIO(template % (
- '''
- <permission
- id="y.x"
- title="XY"
- description="Allow XY." />
-
- <adapter
- factory="zope.app.component.tests.adapter.A1"
- provides="zope.app.component.tests.adapter.I1"
- for="zope.app.component.tests.components.IContent"
- permission="y.x"
- trusted="yes"
- />
- '''
- )))
-
- # With an unproxied object, business as usual
- ob = Content()
- self.assertEqual(type(I1(ob)).__name__, 'A1')
-
- # Now with a proxied object:
- from zope.security.checker import ProxyFactory
- p = ProxyFactory(ob)
-
- # we get a proxied adapter:
- a = I1(p)
- from zope.security.proxy import Proxy
- self.assertEqual(type(a), Proxy)
-
- # behind the security proxy is location proxy
- # if non-public permission is used
- from zope.security.proxy import removeSecurityProxy
- a = removeSecurityProxy(a)
- self.assertEqual(type(a).__name__, 'LocationProxy')
- self.assert_(a.context[0] is ob)
-
-
- def testTrustedAdapterWithPublicPermission(self):
- # Full import is critical!
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A1"
- provides="zope.app.component.tests.adapter.I1"
- for="zope.app.component.tests.components.IContent"
- permission="zope.Public"
- trusted="yes"
- />
- '''
- )))
-
- # With an unproxied object, business as usual
- ob = Content()
- self.assertEqual(type(I1(ob)).__name__, 'A1')
-
- # Now with a proxied object:
- from zope.security.checker import ProxyFactory
- p = ProxyFactory(ob)
-
- # we get a proxied adapter:
- a = I1(p)
- from zope.security.proxy import Proxy
- self.assertEqual(type(a), Proxy)
-
- # behind the security proxy is no location proxy
- from zope.security.proxy import removeSecurityProxy
- a = removeSecurityProxy(a)
- self.assertEqual(type(a).__name__, 'A1')
- self.assert_(a.context[0] is ob)
-
-
- def testLocatableTrustedAdapter(self):
- # Full import is critical!
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A1"
- provides="zope.app.component.tests.adapter.I1"
- for="zope.app.component.tests.components.IContent"
- trusted="yes"
- locate="yes"
- />
- '''
- )))
-
- # With an unproxied object, business as usual
- ob = Content()
- self.assertEqual(type(I1(ob)).__name__, 'A1')
-
- # Now with a proxied object:
- from zope.security.checker import ProxyFactory
- p = ProxyFactory(ob)
-
- # we get a proxied adapter:
- a = I1(p)
- from zope.security.proxy import Proxy
- self.assertEqual(type(a), Proxy)
-
- # behind the security proxy is always location proxy:
- from zope.security.proxy import removeSecurityProxy
- a = removeSecurityProxy(a)
- self.assertEqual(type(a).__name__, 'LocationProxy')
- self.assert_(a.context[0] is ob)
-
- def testAdapter_w_multiple_factories(self):
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A1
- zope.app.component.tests.adapter.A2
- zope.app.component.tests.adapter.A3
- "
- provides="zope.app.component.tests.components.IApp"
- for="zope.app.component.tests.components.IContent"
- />
- '''
- )))
-
- # The resulting adapter should be an A3, around an A2, around
- # an A1, andround the content:
-
- content = Content()
- a3 = IApp(content)
- self.assertEqual(a3.__class__, A3)
- a2 = a3.context[0]
- self.assertEqual(a2.__class__, A2)
- a1 = a2.context[0]
- self.assertEqual(a1.__class__, A1)
- self.assertEqual(a1.context[0], content)
-
- def testAdapter_fails_w_no_factories(self):
- self.assertRaises(ConfigurationError,
- xmlconfig,
- StringIO(template % (
- '''
- <adapter
- factory="
- "
- provides="zope.app.component.tests.components.IApp"
- for="zope.app.component.tests.components.IContent"
- />
- '''
- )),
- )
-
- def testAdapterHavingARequiredClass(self):
- xmlconfig(StringIO(template % (
- '''
- <adapter
- for="zope.app.component.tests.components.Content"
- provides="zope.app.component.tests.adapter.I1"
- factory="zope.app.component.tests.adapter.A1"
- />
- '''
- )))
-
- content = Content()
- a1 = zapi.getAdapter(content, I1, '')
- self.assert_(isinstance(a1, A1))
-
- class MyContent:
- implements(IContent)
-
- self.assertRaises(ComponentLookupError, zapi.getAdapter,
- MyContent(), I1, '')
-
-
- def testMultiAdapter(self):
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A3
- "
- provides="zope.app.component.tests.adapter.I3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1
- zope.app.component.tests.adapter.I2"
- />
- '''
- )))
- content = Content()
- a1 = A1()
- a2 = A2()
- a3 = zapi.queryMultiAdapter((content, a1, a2), I3)
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(a3.context, (content, a1, a2))
-
- def testProtectedMultiAdapter(self):
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A3
- "
- provides="zope.app.component.tests.adapter.I3"
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1
- zope.app.component.tests.adapter.I2
- "
- permission="zope.Public"
- />
- '''
- )))
- content = Content()
- a1 = A1()
- a2 = A2()
- a3 = ProxyFactory(zapi.queryMultiAdapter((content, a1, a2), I3))
- self.assertEqual(a3.__class__, A3)
- items = [item[0] for item in getTestProxyItems(a3)]
- self.assertEqual(items, ['f1', 'f2', 'f3'])
-
- def testMultiAdapter_wo_for_or_provides(self):
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A3"
- />
- '''
- )))
-
- content = Content()
- a1 = A1()
- a2 = A2()
- a3 = zapi.queryMultiAdapter((content, a1, a2), I3)
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(a3.context, (content, a1, a2))
-
- def testNullAdapter(self):
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A3"
- provides="zope.app.component.tests.adapter.I3"
- for=""
- />
- '''
- )))
-
- a3 = zapi.queryMultiAdapter((), I3)
- self.assertEqual(a3.__class__, A3)
- self.assertEqual(a3.context, ())
-
- def testMultiAdapterFails_w_multiple_factories(self):
- self.assertRaises(ConfigurationError,
- xmlconfig,
- StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A1
- zope.app.component.tests.adapter.A2
- "
- for="zope.app.component.tests.components.IContent
- zope.app.component.tests.adapter.I1
- zope.app.component.tests.adapter.I2
- "
- provides="zope.app.component.tests.components.IApp"
- />
- '''
- )),
- )
-
- self.assertRaises(ConfigurationError,
- xmlconfig,
- StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.adapter.A1
- zope.app.component.tests.adapter.A2
- "
- for=""
- provides="zope.app.component.tests.components.IApp"
- />
- '''
- )),
- )
-
-
- def testNamedAdapter(self):
- self.testAdapter()
- self.assertEqual(IApp(Content()).__class__, Comp)
- self.assertEqual(zapi.queryAdapter(Content(), IV, 'test'), None)
-
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.components.Comp"
- provides="zope.app.component.tests.components.IApp"
- for="zope.app.component.tests.components.IContent"
- name="test"
- />
- '''
- )))
-
- self.assertEqual(
- zapi.getAdapter(Content(), IApp, "test").__class__, Comp)
-
- def testProtectedAdapter(self):
- self.assertEqual(IV(Content(), None), None)
-
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.components.Comp"
- provides="zope.app.component.tests.components.IApp"
- for="zope.app.component.tests.components.IContent"
- permission="zope.Public"
- />
- '''
- )))
-
- adapter = ProxyFactory(IApp(Content()))
- items = [item[0] for item in getTestProxyItems(adapter)]
- self.assertEqual(items, ['a', 'f'])
- self.assertEqual(removeSecurityProxy(adapter).__class__, Comp)
-
- def testProtectedAdapter_wo_for_or_provides(self):
- self.assertEqual(IV(Content(), None), None)
- xmlconfig(StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.components.Comp"
- permission="zope.Public"
- />
- '''
- )))
-
- adapter = ProxyFactory(IApp(Content()))
- items = [item[0] for item in getTestProxyItems(adapter)]
- self.assertEqual(items, ['a', 'f'])
- self.assertEqual(removeSecurityProxy(adapter).__class__, Comp)
-
- def testAdapterUndefinedPermission(self):
- config = StringIO(template % (
- '''
- <adapter
- factory="zope.app.component.tests.components.Comp"
- provides="zope.app.component.tests.components.IApp"
- for="zope.app.component.tests.components.IContent"
- permission="zope.UndefinedPermission"
- />
- '''
- ))
- self.assertRaises(ValueError, xmlconfig, config, testing=1)
-
- def testUtility(self):
- self.assertEqual(zapi.queryUtility(IV), None)
-
- xmlconfig(StringIO(template % (
- '''
- <utility
- component="zope.app.component.tests.components.comp"
- provides="zope.app.component.tests.components.IApp"
- />
- '''
- )))
-
- self.assertEqual(zapi.getUtility(IApp), comp)
-
- def testUtility_wo_provides(self):
- self.assertEqual(zapi.queryUtility(IV), None)
-
- xmlconfig(StringIO(template % (
- '''
- <utility
- component="zope.app.component.tests.components.comp"
- />
- '''
- )))
-
- self.assertEqual(zapi.getUtility(IApp), comp)
-
- def testUtility_wo_provides_fails_if_no_provides(self):
- try:
- xmlconfig(StringIO(template % (
- '''
- <utility
- component="zope.app.component.tests.adapter.a4"
- />
- '''
- )))
- except ConfigurationError, v:
- self.assert_("Missing 'provides' attribute" in str(v))
-
- def testUtility_wo_provides_fails_if_too_many_provided(self):
- try:
- xmlconfig(StringIO(template % (
- '''
- <utility
- component="zope.app.component.tests.adapter.a5"
- />
- '''
- )))
- except ConfigurationError, v:
- self.assert_("Missing 'provides' attribute" in str(v))
-
- def testUtility_wo_provides_fails_if_no_implemented(self):
- try:
- xmlconfig(StringIO(template % (
- '''
- <utility
- factory="zope.app.component.tests.adapter.A4"
- />
- '''
- )))
- except ConfigurationError, v:
- self.assert_("Missing 'provides' attribute" in str(v))
-
- def testUtility_wo_provides_fails_if_too_many_implemented(self):
- try:
- xmlconfig(StringIO(template % (
- '''
- <utility
- factory="zope.app.component.tests.adapter.A5"
- />
- '''
- )))
- except ConfigurationError, v:
- self.assert_("Missing 'provides' attribute" in str(v))
-
- def testNamedUtility(self):
- self.testUtility()
- self.assertEqual(zapi.queryUtility(IV, 'test'), None)
- xmlconfig(StringIO(template % (
- '''
- <utility
- component="zope.app.component.tests.components.comp"
- provides="zope.app.component.tests.components.IApp"
- name="test"
- />
- '''
- )))
-
- self.assertEqual(zapi.getUtility(IApp, "test"), comp)
-
- def testUtilityFactory(self):
- self.assertEqual(zapi.queryUtility(IV), None)
-
- xmlconfig(StringIO(template % (
- '''
- <utility
- factory="zope.app.component.tests.components.Comp"
- provides="zope.app.component.tests.components.IApp"
- />
- '''
- )))
-
- self.assertEqual(zapi.getUtility(IApp).__class__, Comp)
-
- def testProtectedUtility(self):
- """Test that we can protect a utility.
-
- Also:
- Check that multiple configurations for the same utility and
- don't interfere.
- """
- self.assertEqual(zapi.queryUtility(IV), None)
- xmlconfig(StringIO(template % (
- '''
- <permission id="tell.everyone" title="Yay" />
- <utility
- component="zope.app.component.tests.components.comp"
- provides="zope.app.component.tests.components.IApp"
- permission="tell.everyone"
- />
- <permission id="top.secret" title="shhhh" />
- <utility
- component="zope.app.component.tests.components.comp"
- provides="zope.app.component.tests.components.IAppb"
- permission="top.secret"
- />
- '''
- )))
-
- utility = ProxyFactory(zapi.getUtility(IApp))
- items = getTestProxyItems(utility)
- self.assertEqual(items, [('a', 'tell.everyone'),
- ('f', 'tell.everyone')
- ])
- self.assertEqual(removeSecurityProxy(utility), comp)
-
- def testUtilityUndefinedPermission(self):
- config = StringIO(template % (
- '''
- <utility
- component="zope.app.component.tests.components.comp"
- provides="zope.app.component.tests.components.IApp"
- permission="zope.UndefinedPermission"
- />
- '''
- ))
- self.assertRaises(ValueError, xmlconfig, config,
- testing=1)
-
-
def testView(self):
ob = Ob()
request = Request(IV)
Copied: Zope3/branches/jim-adapter/src/zope/component/meta.zcml (from rev 66842, Zope3/branches/jim-adapter/src/zope/app/component/meta.zcml)
===================================================================
--- Zope3/branches/jim-adapter/src/zope/app/component/meta.zcml 2006-04-11 10:03:07 UTC (rev 66842)
+++ Zope3/branches/jim-adapter/src/zope/component/meta.zcml 2006-04-12 18:27:10 UTC (rev 66900)
@@ -0,0 +1,33 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta">
+
+ <meta:directives namespace="http://namespaces.zope.org/zope">
+
+ <meta:directive
+ name="interface"
+ schema="zope.component.zcml.IInterfaceDirective"
+ handler="zope.component.zcml.interface"
+ />
+
+ <meta:directive
+ name="adapter"
+ schema="zope.component.zcml.IAdapterDirective"
+ handler="zope.component.zcml.adapter"
+ />
+
+ <meta:directive
+ name="subscriber"
+ schema="zope.component.zcml.ISubscriberDirective"
+ handler="zope.component.zcml.subscriber"
+ />
+
+ <meta:directive
+ name="utility"
+ schema="zope.component.zcml.IUtilityDirective"
+ handler="zope.component.zcml.utility"
+ />
+
+ </meta:directives>
+
+</configure>
Modified: Zope3/branches/jim-adapter/src/zope/component/tests.py
===================================================================
--- Zope3/branches/jim-adapter/src/zope/component/tests.py 2006-04-12 18:09:30 UTC (rev 66899)
+++ Zope3/branches/jim-adapter/src/zope/component/tests.py 2006-04-12 18:27:10 UTC (rev 66900)
@@ -796,6 +796,13 @@
import zope.event
zope.event.subscribers.pop()
+def clearZCML(test=None):
+ tearDown()
+ setUp()
+
+ from zope.configuration.xmlconfig import XMLConfig
+ XMLConfig('meta.zcml', component)()
+
def test_suite():
checker = renormalizing.RENormalizing([
(re.compile('at 0x[0-9a-f]+'), 'at <SOME ADDRESS>'),
@@ -803,6 +810,8 @@
return unittest.TestSuite((
doctest.DocTestSuite(setUp=setUp, tearDown=tearDown),
+ doctest.DocTestSuite('zope.component.interface',
+ setUp=setUp, tearDown=tearDown),
doctest.DocFileSuite('README.txt',
setUp=setUp, tearDown=tearDown),
doctest.DocFileSuite('socketexample.txt',
@@ -813,7 +822,7 @@
tearDown=tearDownRegistryTests),
doctest.DocFileSuite('event.txt',
setUp=setUp, tearDown=tearDown),
- doctest.DocTestSuite('zope.component.interface',
+ doctest.DocFileSuite('zcml.txt',
setUp=setUp, tearDown=tearDown),
))
Copied: Zope3/branches/jim-adapter/src/zope/component/zcml.txt (from rev 66842, Zope3/branches/jim-adapter/src/zope/app/component/tests/test_directives.py)
===================================================================
--- Zope3/branches/jim-adapter/src/zope/app/component/tests/test_directives.py 2006-04-11 10:03:07 UTC (rev 66842)
+++ Zope3/branches/jim-adapter/src/zope/component/zcml.txt 2006-04-12 18:27:10 UTC (rev 66900)
@@ -0,0 +1,1013 @@
+ZCML directives
+===============
+
+Components may be registered using the registration API exposed by
+``zope.component`` (provideAdapter, provideUtility, etc.). They may
+also be registered using configuration files. The common way to do
+that is by using ZCML (Zope Configuration Markup Language), an XML
+spelling of component registration.
+
+In ZCML, each XML element is a *directive*. There are different
+top-level directives that let us register components. We will
+introduce them one by one here.
+
+This helper will let us easily execute ZCML snippets:
+
+ >>> from cStringIO import StringIO
+ >>> from zope.configuration.xmlconfig import xmlconfig
+ >>> def runSnippet(snippet):
+ ... template = """\
+ ... <configure xmlns='http://namespaces.zope.org/zope'
+ ... i18n_domain="zope">
+ ... %s
+ ... </configure>"""
+ ... xmlconfig(StringIO(template % snippet))
+
+adapter
+-------
+
+Adapters play a key role in the Component Architecture. In ZCML, they
+are registered with the <adapter /> directive.
+
+ >>> from zope.app.component.tests.adapter import A1, A2, A3, Handler
+ >>> from zope.app.component.tests.adapter import I1, I2, I3, IS
+ >>> from zope.app.component.tests.components import IContent, Content, Comp, comp
+
+Before we register the first test adapter, we can verify that adapter
+lookup doesn't work yet:
+
+ >>> from zope.component.tests import clearZCML
+ >>> clearZCML()
+ >>> from zope.app.component.tests.components import IApp
+ >>> IApp(Content(), None) is None
+ True
+
+Then we register the adapter and see that the lookup works:
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.components.Comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... for="zope.app.component.tests.components.IContent"
+ ... />''')
+
+ >>> IApp(Content()).__class__
+ <class 'zope.app.component.tests.components.Comp'>
+
+It is also possible to give adapters names. Then the combination of
+required interface, provided interface and name makes the adapter
+lookup unique. The name is supplied using the ``name`` argument to
+the <adapter /> directive:
+
+ >>> from zope.component.tests import clearZCML
+ >>> clearZCML()
+ >>> import zope.component
+ >>> zope.component.queryAdapter(Content(), IApp, 'test') is None
+ True
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.components.Comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... for="zope.app.component.tests.components.IContent"
+ ... name="test"
+ ... />''')
+
+ >>> zope.component.getAdapter(Content(), IApp, 'test').__class__
+ <class 'zope.app.component.tests.components.Comp'>
+
+Adapter factories
+~~~~~~~~~~~~~~~~~
+
+It is possible to supply more than one adapter factory. In this case,
+during adapter lookup each factory will be called and the return value
+will be given to the next factory. The return value of the last
+factory is returned as the result of the adapter lookup. For examle:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.adapter.A1
+ ... zope.app.component.tests.adapter.A2
+ ... zope.app.component.tests.adapter.A3"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... for="zope.app.component.tests.components.IContent"
+ ... />''')
+
+The resulting adapter is an A3, around an A2, around an A1, around the
+adapted object:
+
+ >>> content = Content()
+ >>> a3 = IApp(content)
+ >>> a3.__class__ is A3
+ True
+
+ >>> a2 = a3.context[0]
+ >>> a2.__class__ is A2
+ True
+
+ >>> a1 = a2.context[0]
+ >>> a1.__class__ is A1
+ True
+
+ >>> a1.context[0] is content
+ True
+
+Of course, if no factory is provided at all, we will get an error:
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory=""
+ ... provides="zope.app.component.tests.components.IApp"
+ ... for="zope.app.component.tests.components.IContent"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-8.8
+ ValueError: No factory specified
+
+Declaring ``for`` and ``provides`` 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::
+
+ >>> clearZCML()
+ >>> IApp(Content(), None) is None
+ True
+
+ >>> runSnippet('''
+ ... <adapter factory="zope.app.component.tests.components.Comp" />''')
+
+ >>> IApp(Content()).__class__
+ <class 'zope.app.component.tests.components.Comp'>
+
+Of course, if the adapter has no ``implements()`` declaration, ZCML
+can't figure out what it provides:
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.adapter.A4"
+ ... for="zope.app.component.tests.components.IContent"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-7.8
+ TypeError: Missing 'provides' attribute
+
+On the other hand, if the factory implements more than one interface,
+ZCML can't figure out what it should provide either:
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.adapter.A5"
+ ... for="zope.app.component.tests.components.IContent"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-7.8
+ TypeError: Missing 'provides' attribute
+
+A not so common edge case is registering adapters directly for
+classes, not for interfaces. For example:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.app.component.tests.components.Content"
+ ... provides="zope.app.component.tests.adapter.I1"
+ ... factory="zope.app.component.tests.adapter.A1"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = zope.component.getAdapter(content, I1, '')
+ >>> isinstance(a1, A1)
+ True
+
+This time, any object providing ``IContent`` won't work if it's not an
+instance of the ``Content`` class:
+
+ >>> import zope.interface
+ >>> class MyContent:
+ ... zope.interface.implements(IContent)
+ >>> zope.component.getAdapter(MyContent(), I1, '') # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: ...
+
+Multi-adapters
+~~~~~~~~~~~~~~
+
+Conventional adapters adapt one object to provide another interface.
+Multi-adapters adapt several objects at once:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1
+ ... zope.app.component.tests.adapter.I2"
+ ... provides="zope.app.component.tests.adapter.I3"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = A1()
+ >>> a2 = A2()
+ >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3)
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1, a2)
+ True
+
+You can even adapt an empty list of objects (we call this a
+null-adapter):
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for=""
+ ... provides="zope.app.component.tests.adapter.I3"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... />''')
+
+ >>> a3 = zope.component.queryMultiAdapter((), I3)
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == ()
+ True
+
+Even with multi-adapters, ZCML can figure out the ``for`` and
+``provides`` parameters from the Python declarations:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter factory="zope.app.component.tests.adapter.A3" />''')
+
+ >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3)
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1, a2)
+ True
+
+Chained factories are not supported for multi-adapters, though:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1
+ ... zope.app.component.tests.adapter.I2"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... factory="zope.app.component.tests.adapter.A1
+ ... zope.app.component.tests.adapter.A2"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-11.8
+ ValueError: Can't use multiple factories and multiple for
+
+And neither for null-adapters:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for=""
+ ... provides="zope.app.component.tests.components.IApp"
+ ... factory="zope.app.component.tests.adapter.A1
+ ... zope.app.component.tests.adapter.A2"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-9.8
+ ValueError: Can't use multiple factories and multiple for
+
+Protected adapters
+~~~~~~~~~~~~~~~~~~
+
+Adapters can be protected with a permission. First we have to define
+a permission for which we'll have to register the <permission />
+directive:
+
+ >>> clearZCML()
+ >>> IApp(Content(), None) is None
+ True
+
+ >>> import zope.app.security
+ >>> from zope.configuration.xmlconfig import XMLConfig
+ >>> XMLConfig('meta.zcml', zope.app.security)()
+ >>> runSnippet('''
+ ... <permission
+ ... id="y.x"
+ ... title="XY"
+ ... description="Allow XY."
+ ... />
+ ... <adapter
+ ... factory="zope.app.component.tests.components.Comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... for="zope.app.component.tests.components.IContent"
+ ... permission="y.x"
+ ... />''')
+
+We see that the adapter is a location proxy now so that the
+appropriate permissions can be found from the context:
+
+ >>> IApp(Content()).__class__
+ <class 'zope.app.component.tests.components.Comp'>
+ >>> type(IApp(Content()))
+ <class 'zope.location.location.LocationProxy'>
+
+We can also go about it a different way. Let's make a public adapter
+and wrap the adapter in a security proxy. That often happens when
+an adapter is turned over to untrusted code:
+
+ >>> clearZCML()
+ >>> IApp(Content(), None) is None
+ True
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.components.Comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... for="zope.app.component.tests.components.IContent"
+ ... permission="zope.Public"
+ ... />''')
+
+ >>> from zope.security.checker import ProxyFactory
+ >>> adapter = ProxyFactory(IApp(Content()))
+ >>> from zope.security.proxy import getTestProxyItems
+ >>> items = [item[0] for item in getTestProxyItems(adapter)]
+ >>> items
+ ['a', 'f']
+
+ >>> from zope.security.proxy import removeSecurityProxy
+ >>> removeSecurityProxy(adapter).__class__ is Comp
+ True
+
+Of course, this still works when we let the ZCML directive handler
+figure out ``for`` and ``provides`` from the Python declarations:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.components.Comp"
+ ... permission="zope.Public"
+ ... />''')
+
+ >>> adapter = ProxyFactory(IApp(Content()))
+ >>> [item[0] for item in getTestProxyItems(adapter)]
+ ['a', 'f']
+ >>> removeSecurityProxy(adapter).__class__ is Comp
+ True
+
+It also works with multi adapters:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... provides="zope.app.component.tests.adapter.I3"
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1
+ ... zope.app.component.tests.adapter.I2"
+ ... permission="zope.Public"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = A1()
+ >>> a2 = A2()
+ >>> a3 = ProxyFactory(zope.component.queryMultiAdapter((content, a1, a2), I3))
+ >>> a3.__class__ == A3
+ True
+ >>> [item[0] for item in getTestProxyItems(a3)]
+ ['f1', 'f2', 'f3']
+
+It's probably not worth mentioning, but when we try to protect an
+adapter with a permission that doesn't exist, we'll obviously get an
+error:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.app.component.tests.components.Comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... for="zope.app.component.tests.components.IContent"
+ ... permission="zope.UndefinedPermission"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ConfigurationExecutionError: exceptions.ValueError: ('Undefined permission id', 'zope.UndefinedPermission')
+ in:
+ File "<string>", line 4.2-9.8
+ Could not read source.
+
+Trusted adapters
+~~~~~~~~~~~~~~~~
+
+Trusted adapters are adapters that are trusted to do anything with the
+objects they are given so that these objects are not security-proxied.
+They are registered using the ``trusted`` argument to the <adapter />
+directive:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.app.component.tests.components.IContent"
+ ... provides="zope.app.component.tests.adapter.I1"
+ ... factory="zope.app.component.tests.adapter.A1"
+ ... trusted="yes"
+ ... />''')
+
+With an unproxied object, it's business as usual:
+
+ >>> ob = Content()
+ >>> type(I1(ob)) is A1
+ True
+
+With a security-proxied object, however, we get a security-proxied
+adapter:
+
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+While the adapter is security-proxied, the object it adapts is now
+proxy-free. The adapter has umlimited access to it:
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a) is A1
+ True
+ >>> a.context[0] is ob
+ True
+
+We can also protect the trusted adapter with a permission:
+
+ >>> clearZCML()
+ >>> XMLConfig('meta.zcml', zope.app.security)()
+ >>> runSnippet('''
+ ... <permission
+ ... id="y.x"
+ ... title="XY"
+ ... description="Allow XY."
+ ... />
+ ... <adapter
+ ... for="zope.app.component.tests.components.IContent"
+ ... provides="zope.app.component.tests.adapter.I1"
+ ... factory="zope.app.component.tests.adapter.A1"
+ ... permission="y.x"
+ ... trusted="yes"
+ ... />''')
+
+Again, with an unproxied object, it's business as usual:
+
+ >>> ob = Content()
+ >>> type(I1(ob)) is A1
+ True
+
+With a security-proxied object, we again get a security-proxied
+adapter:
+
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+Since we protected the adapter with a permission, we now encounter a
+location proxy behind the security proxy:
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a)
+ <class 'zope.location.location.LocationProxy'>
+ >>> a.context[0] is ob
+ True
+
+There's one exception to all of this: When you use the public
+permission (``zope.Public``), there will be no location proxy:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.app.component.tests.components.IContent"
+ ... provides="zope.app.component.tests.adapter.I1"
+ ... factory="zope.app.component.tests.adapter.A1"
+ ... permission="zope.Public"
+ ... trusted="yes"
+ ... />''')
+
+ >>> ob = Content()
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a) is A1
+ True
+
+We can also explicitply pass the ``locate`` argument to make sure we
+get location proxies:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.app.component.tests.components.IContent"
+ ... provides="zope.app.component.tests.adapter.I1"
+ ... factory="zope.app.component.tests.adapter.A1"
+ ... trusted="yes"
+ ... locate="yes"
+ ... />''')
+
+ >>> ob = Content()
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a)
+ <class 'zope.location.location.LocationProxy'>
+
+
+subscriber
+----------
+
+With the <subscriber /> directive you can register subscription
+adapters or event subscribers with the adapter registry. Consider
+this very typical example of a <subscriber /> directive:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.app.component.tests.adapter.IS"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = A1()
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1)
+ True
+
+Note how ZCML provides some additional information when registering
+components, such as the ZCML filename and line numbers:
+
+ >>> gsm = zope.component.getGlobalSiteManager()
+ >>> doc = [reg.info for reg in gsm.registeredSubscriptionAdapters()
+ ... if reg.provided is IS][0]
+ >>> print doc
+ File "<string>", line 4.2-9.8
+ Could not read source.
+
+The "fun" behind subscription adapters/subscribers is that when
+several ones are declared for the same for/provides, they are all
+found. With regular adapters, the most specific one (and in doubt the
+one registered last) wins. Consider these two subscribers:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.app.component.tests.adapter.IS"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1"
+ ... />
+ ... <subscriber
+ ... provides="zope.app.component.tests.adapter.IS"
+ ... factory="zope.app.component.tests.adapter.A2"
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1"
+ ... />''')
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> len(subscribers)
+ 2
+ >>> sorted([a.__class__.__name__ for a in subscribers])
+ ['A2', 'A3']
+
+Declaring ``for`` and ``provides`` in Python
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Like the <adapter /> directive, the <subscriber /> directive can
+figure out from the in-line Python declaration (using
+``zope.component.adapts()`` or ``zope.component.adapter()``) what the
+subscriber should be registered for:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.app.component.tests.adapter.IS"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... />''')
+
+ >>> content = Content()
+ >>> a2 = A2()
+ >>> subscribers = zope.component.subscribers((content, a1, a2), IS)
+
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1, a2)
+ True
+
+In the same way the directive can figure out what a subscriber
+provides:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber handler="zope.app.component.tests.adapter.A3" />''')
+
+ >>> sm = zope.component.getSiteManager()
+ >>> a3 = sm.adapters.subscriptions((IContent, I1, I2), None)[0]
+ >>> a3 is A3
+ True
+
+A not so common edge case is declaring subscribers directly for
+classes, not for interfaces. For example:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... for="zope.app.component.tests.components.Content"
+ ... provides="zope.app.component.tests.adapter.I1"
+ ... factory="zope.app.component.tests.adapter.A1"
+ ... />''')
+
+ >>> subs = list(zope.component.subscribers((Content(),), I1))
+ >>> isinstance(subs[0], A1)
+ True
+
+This time, any object providing ``IContent`` won't work if it's not an
+instance of the ``Content`` class:
+
+ >>> list(zope.component.subscribers((MyContent(),), I1))
+ []
+
+Protected subscribers
+~~~~~~~~~~~~~~~~~~~~~
+
+Subscribers can also be protected with a permission. First we have to
+define a permission for which we'll have to register the <permission />
+directive:
+
+ >>> clearZCML()
+ >>> XMLConfig('meta.zcml', zope.app.security)()
+ >>> runSnippet('''
+ ... <permission
+ ... id="y.x"
+ ... title="XY"
+ ... description="Allow XY."
+ ... />
+ ... <subscriber
+ ... provides="zope.app.component.tests.adapter.IS"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1"
+ ... permission="y.x"
+ ... />''')
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> type(a3)
+ <class 'zope.location.location.LocationProxy'>
+ >>> a3.context == (content, a1)
+ True
+
+Trusted subscribers
+~~~~~~~~~~~~~~~~~~~
+
+Like trusted adapters, trusted subscribers are subscribers that are
+trusted to do anything with the objects they are given so that these
+objects are not security-proxied. In analogy to the <adapter />
+directive, they are registered using the ``trusted`` argument to the
+<subscriber /> directive:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.app.component.tests.adapter.IS"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1"
+ ... trusted="yes"
+ ... />''')
+
+With an unproxied object, it's business as usual:
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1)
+ True
+ >>> type(a3) is A3
+ True
+
+Now with a proxied object. We will see that the subscriber has
+unproxied access to it, but the subscriber itself is proxied:
+
+ >>> p = ProxyFactory(content)
+ >>> a3 = zope.component.subscribers((p, a1), IS)[0]
+ >>> type(a3)
+ <type 'zope.security._proxy._Proxy'>
+
+There's no location proxy behind the security proxy:
+
+ >>> removeSecurityProxy(a3).context[0] is content
+ True
+ >>> type(removeSecurityProxy(a3)) is A3
+ True
+
+If you want the trusted subscriber to be located, you'll also have to
+use the ``locate`` argument:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.app.component.tests.adapter.IS"
+ ... factory="zope.app.component.tests.adapter.A3"
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1"
+ ... trusted="yes"
+ ... locate="yes"
+ ... />''')
+
+Again, it's business as usual with an unproxied object:
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1)
+ True
+ >>> type(a3) is A3
+ True
+
+With a proxied object, we again get a security-proxied subscriber:
+
+ >>> p = ProxyFactory(content)
+ >>> a3 = zope.component.subscribers((p, a1), IS)[0]
+
+ >>> type(a3)
+ <type 'zope.security._proxy._Proxy'>
+
+ >>> removeSecurityProxy(a3).context[0] is content
+ True
+
+However, thanks to the ``locate`` argument, we now have a location
+proxy behind the security proxy:
+
+ >>> type(removeSecurityProxy(a3))
+ <class 'zope.location.location.LocationProxy'>
+
+Event subscriber (handlers)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes, subscribers don't need to be adapters that actually provide
+anything. It's enough that a callable is called for a certain event.
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... for="zope.app.component.tests.components.IContent
+ ... zope.app.component.tests.adapter.I1"
+ ... handler="zope.app.component.tests.adapter.Handler"
+ ... />''')
+
+In this case, simply getting the subscribers is enough to invoke them:
+
+ >>> list(zope.component.subscribers((content, a1), None))
+ []
+ >>> content.args == ((a1,),)
+ True
+
+
+utility
+-------
+
+Apart from adapters (and subscription adapters), the Component
+Architecture knows a second kind of component: utilities. They are
+registered using the <utility /> directive.
+
+Before we register the first test utility, we can verify that utility
+lookup doesn't work yet:
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp) is None
+ True
+
+Then we register the utility:
+
+ >>> runSnippet('''
+ ... <utility
+ ... component="zope.app.component.tests.components.comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... />''')
+ >>> zope.component.getUtility(IApp) is comp
+ True
+
+Like adapters, utilities can also have names. There can be more than
+one utility registered for a certain interface, as long as they each
+have a different name.
+
+First, we make sure that there's no utility yet:
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp, 'test') is None
+ True
+
+Then we register it:
+
+ >>> runSnippet('''
+ ... <utility
+ ... component="zope.app.component.tests.components.comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... name="test"
+ ... />''')
+ >>> zope.component.getUtility(IApp, 'test') is comp
+ True
+
+Utilities can also be registered from a factory. In this case, the
+ZCML handler calls the factory (without any arguments) and registers
+the returned value as a utility. Typically, you'd pass a class for
+the factory:
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp) is None
+ True
+
+ >>> runSnippet('''
+ ... <utility
+ ... factory="zope.app.component.tests.components.Comp"
+ ... provides="zope.app.component.tests.components.IApp"
+ ... />''')
+ >>> zope.component.getUtility(IApp).__class__ is Comp
+ True
+
+Declaring ``provides`` in Python
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Like other directives, <utility /> can also figure out which interface
+a utility provides from the Python declaration:
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp) is None
+ True
+
+ >>> runSnippet('''
+ ... <utility component="zope.app.component.tests.components.comp" />''')
+ >>> zope.component.getUtility(IApp) is comp
+ True
+
+It won't work if the component that is to be registered doesn't
+provide anything:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility component="zope.app.component.tests.adapter.a4" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.61
+ TypeError: Missing 'provides' attribute
+
+Or if more than one interface is provided (then the ZCML directive
+handler doesn't know under which the utility should be registered):
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility component="zope.app.component.tests.adapter.a5" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.61
+ TypeError: Missing 'provides' attribute
+
+We can repeat the same drill for utility factories:
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility component="zope.app.component.tests.adapter.A4" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.61
+ TypeError: Missing 'provides' attribute
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility component="zope.app.component.tests.adapter.A5" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.61
+ TypeError: Missing 'provides' attribute
+
+Protected utilities
+~~~~~~~~~~~~~~~~~~~
+
+ def testProtectedUtility(self):
+ """Test that we can protect a utility.
+
+ Also:
+ Check that multiple configurations for the same utility and
+ don't interfere.
+ """
+ self.assertEqual(zope.component.queryUtility(IV), None)
+ xmlconfig(StringIO(template % (
+ '''
+ <permission id="tell.everyone" title="Yay" />
+ <utility
+ component="zope.app.component.tests.components.comp"
+ provides="zope.app.component.tests.components.IApp"
+ permission="tell.everyone"
+ />
+ <permission id="top.secret" title="shhhh" />
+ <utility
+ component="zope.app.component.tests.components.comp"
+ provides="zope.app.component.tests.components.IAppb"
+ permission="top.secret"
+ />
+ '''
+ )))
+
+ utility = ProxyFactory(zope.component.getUtility(IApp))
+ items = getTestProxyItems(utility)
+ self.assertEqual(items, [('a', 'tell.everyone'),
+ ('f', 'tell.everyone')
+ ])
+ self.assertEqual(removeSecurityProxy(utility), comp)
+
+ def testUtilityUndefinedPermission(self):
+ config = StringIO(template % (
+ '''
+ <utility
+ component="zope.app.component.tests.components.comp"
+ provides="zope.app.component.tests.components.IApp"
+ permission="zope.UndefinedPermission"
+ />
+ '''
+ ))
+ self.assertRaises(ValueError, xmlconfig, config,
+ testing=1)
+
+interface
+---------
+
+The <interface /> directive lets us register an interface. Interfaces
+are registered as named utilities. We therefore needn't go though all
+the lookup details again, it is sufficient to see whether the
+directive handler emits the right actions.
+
+First we provide a stub configuration context:
+
+ >>> import re, pprint
+ >>> atre = re.compile(' at [0-9a-fA-Fx]+')
+ >>> class Context(object):
+ ... actions = ()
+ ... def action(self, discriminator, callable, args):
+ ... self.actions += ((discriminator, callable, args), )
+ ... def __repr__(self):
+ ... stream = StringIO()
+ ... pprinter = pprint.PrettyPrinter(stream=stream, width=60)
+ ... pprinter.pprint(self.actions)
+ ... r = stream.getvalue()
+ ... return (''.join(atre.split(r))).strip()
+ >>> context = Context()
+
+Then we provide a test interface that we'd like to register:
+
+ >>> from zope.interface import Interface
+ >>> class I(Interface):
+ ... pass
+
+It doesn't yet provide ``ITestType``:
+
+ >>> from zope.component.tests import ITestType
+ >>> ITestType.providedBy(I)
+ False
+
+However, after calling the directive handler...
+
+ >>> from zope.component.zcml import interface
+ >>> interface(context, I, ITestType)
+ >>> context
+ ((None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass __builtin__.I>,
+ <InterfaceClass zope.component.tests.ITestType>)),)
+
+...it does provide ``ITestType``:
+
+ >>> from zope.interface.interfaces import IInterface
+ >>> ITestType.extends(IInterface)
+ True
+ >>> IInterface.providedBy(I)
+ True
More information about the Zope3-Checkins
mailing list