[Zope3-checkins] SVN: Zope3/trunk/ Added `interface` attribute to
the skin and layer directive, so that we
Stephan Richter
srichter at cosmos.phy.tufts.edu
Fri Sep 17 17:39:28 EDT 2004
Log message for revision 27628:
Added `interface` attribute to the skin and layer directive, so that we
can specify interfaces directly. I hope this will help us in debugging the
skin problem. I also added some better tests for the directives.
Changed:
U Zope3/trunk/doc/CHANGES.txt
U Zope3/trunk/src/zope/app/publisher/browser/metaconfigure.py
U Zope3/trunk/src/zope/app/publisher/browser/metadirectives.py
U Zope3/trunk/src/zope/app/publisher/browser/tests/test_directives.py
-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt 2004-09-17 21:34:39 UTC (rev 27627)
+++ Zope3/trunk/doc/CHANGES.txt 2004-09-17 21:39:28 UTC (rev 27628)
@@ -14,20 +14,24 @@
mainly means that layers and skins became adapters. Here are some
other points:
- o Backward-compatibility of the Zope component API and the browser
+ + Backward-compatibility of the Zope component API and the browser
directives was ensured.
- o The `setPresentationSkin()` and `getPresentationSkin()` directives
+ + The `setPresentationSkin()` and `getPresentationSkin()` directives
of `IRequest` objects were removed. They were not used anywhere but
testing anyways.
- o The default skin is now just an adapter from `IRequest` to
+ + The default skin is now just an adapter from `IRequest` to
`IDefaultSkin`. You have to use `adapter_service.lookup()`, since
the returned component is a skin interface and not a factory.
- o The adapter service can now provide adapters from objects as well,
+ + The adapter service can now provide adapters from objects as well,
like it was possible in the presentation service before.
+ + Gave `browser:skin` and `browser:layer` directive a new attribute
+ `interface`, which allows you to directly specify the skin's or
+ layer's interface instead of autogenerating it.
+
- Added `apidoc:rootModule` directive, so that third-party products can
add their packages to the class browser documentation module.
Modified: Zope3/trunk/src/zope/app/publisher/browser/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/publisher/browser/metaconfigure.py 2004-09-17 21:34:39 UTC (rev 27627)
+++ Zope3/trunk/src/zope/app/publisher/browser/metaconfigure.py 2004-09-17 21:39:28 UTC (rev 27628)
@@ -16,6 +16,7 @@
$Id$
"""
from zope.component.interfaces import IDefaultViewName
+from zope.configuration.exceptions import ConfigurationError
from zope.interface.interface import InterfaceClass
from zope.publisher.interfaces.browser import ILayer, ISkin, IDefaultSkin
from zope.publisher.interfaces.browser import IBrowserRequest
@@ -46,24 +47,106 @@
sys.modules['zope.app.skins'] = skins
-def layer(_context, name, base=IBrowserRequest):
- if ',' in name:
+def layer(_context, name=None, interface=None, base=IBrowserRequest):
+ """Provides a new layer.
+
+ >>> class Context(object):
+ ... info = u'doc'
+ ... def __init__(self): self.actions = []
+ ... def action(self, **kw): self.actions.append(kw)
+
+ Possibility 1: The Old Way
+ --------------------------
+
+ >>> context = Context()
+ >>> layer(context, u'layer1')
+ >>> iface = context.actions[0]['args'][1]
+ >>> iface.getName()
+ u'layer1'
+ >>> iface.__bases__
+ (<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,)
+ >>> hasattr(sys.modules['zope.app.layers'], 'layer1')
+ True
+
+ >>> del sys.modules['zope.app.layers'].layer1
+
+ Possibility 2: Providing a custom base interface
+ ------------------------------------------------
+
+ >>> class BaseLayer(IBrowserRequest):
+ ... pass
+ >>> context = Context()
+ >>> layer(context, u'layer1', base=BaseLayer)
+ >>> iface = context.actions[0]['args'][1]
+ >>> iface.getName()
+ u'layer1'
+ >>> iface.__bases__
+ (<InterfaceClass zope.app.publisher.browser.metaconfigure.BaseLayer>,)
+ >>> hasattr(sys.modules['zope.app.layers'], 'layer1')
+ True
+
+ >>> del sys.modules['zope.app.layers'].layer1
+
+ Possibility 3: Define a Layer just through an Interface
+ -------------------------------------------------------
+
+ >>> class layer1(IBrowserRequest):
+ ... pass
+ >>> context = Context()
+ >>> layer(context, interface=layer1)
+ >>> context.actions[0]['args'][1] is layer1
+ True
+ >>> hasattr(sys.modules['zope.app.layers'], 'layer1')
+ False
+
+ Here are some disallowed configurations.
+
+ >>> context = Context()
+ >>> layer(context, 'foo,bar')
+ Traceback (most recent call last):
+ ...
+ TypeError: Commas are not allowed in layer names.
+ >>> layer(context)
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: You must specify the 'name' or 'interface' attribute.
+ >>> layer(context, base=BaseLayer)
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: You must specify the 'name' or 'interface' attribute.
+
+ >>> layer(context, interface=layer1, base=BaseLayer)
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: You cannot specify the 'interface' and 'base' together.
+ """
+ if name is not None and ',' in name:
raise TypeError("Commas are not allowed in layer names.")
+ if name is None and interface is None:
+ raise ConfigurationError(
+ "You must specify the 'name' or 'interface' attribute.")
+ if interface is not None and base is not IBrowserRequest:
+ raise ConfigurationError(
+ "You cannot specify the 'interface' and 'base' together.")
- layer = InterfaceClass(name, (base, ),
- __doc__='Layer: %s' %name,
- __module__='zope.app.layers')
+ if interface is None:
+ interface = InterfaceClass(name, (base, ),
+ __doc__='Layer: %s' %name,
+ __module__='zope.app.layers')
+ # Add the layer to the skins module.
+ # Note: We have to do this immediately, so that directives using the
+ # InterfaceField can find the layer.
+ setattr(layers, name, interface)
+ path = 'zope.app.layers.'+name
+ else:
+ path = name = interface.__module__ + '.' + interface.getName()
- # Add the layer to the skins module.
- # Note: We have to do this immediately, so that directives using the
- # InterfaceField can find the layer.
- setattr(layers, name, layer)
# Register the layer interface as an interface
_context.action(
- discriminator = ('interface', 'zope.app.layers.'+name),
+ discriminator = ('interface', path),
callable = provideInterface,
- args = ('zope.app.layers.'+name, layer),
+ args = (path, interface),
kw = {'info': _context.info}
)
@@ -71,22 +154,90 @@
_context.action(
discriminator = ('layer', name),
callable = provideInterface,
- args = (name, layer, ILayer, _context.info)
+ args = (name, interface, ILayer, _context.info)
)
-def skin(_context, name, layers):
+def skin(_context, name=None, interface=None, layers=None):
+ """Provides a new layer.
- skin = InterfaceClass(name, layers,
- __doc__='Skin: %s' %name,
- __module__='zope.app.skins')
+ >>> import pprint
+ >>> class Context(object):
+ ... info = u'doc'
+ ... def __init__(self): self.actions = []
+ ... def action(self, **kw): self.actions.append(kw)
+ >>> class Layer1(IBrowserRequest): pass
+ >>> class Layer2(IBrowserRequest): pass
+
+ Possibility 1: The Old Way
+ --------------------------
+
+ >>> context = Context()
+ >>> skin(context, u'skin1', layers=[Layer1, Layer2])
+ >>> iface = context.actions[0]['args'][1]
+ >>> iface.getName()
+ u'skin1'
+ >>> pprint.pprint(iface.__bases__)
+ (<InterfaceClass zope.app.publisher.browser.metaconfigure.Layer1>,
+ <InterfaceClass zope.app.publisher.browser.metaconfigure.Layer2>)
+ >>> hasattr(sys.modules['zope.app.skins'], 'skin1')
+ True
+
+ >>> del sys.modules['zope.app.skins'].skin1
+
+ Possibility 2: Just specify an interface
+ ----------------------------------------
+
+ >>> class skin1(Layer1, Layer2):
+ ... pass
+
+ >>> context = Context()
+ >>> skin(context, interface=skin1)
+ >>> context.actions[0]['args'][1] is skin1
+ True
+
+ Here are some disallowed configurations.
+
+ >>> context = Context()
+ >>> skin(context)
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: You must specify the 'name' or 'interface' attribute.
+ >>> skin(context, layers=[Layer1])
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: You must specify the 'name' or 'interface' attribute.
+
+ >>> skin(context, name=u'skin1')
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: You must specify the 'name' and 'layers' attribute.
+ """
+ if name is None and interface is None:
+ raise ConfigurationError(
+ "You must specify the 'name' or 'interface' attribute.")
+ if (name is not None and layers is None) or \
+ (name is None and layers is not None):
+ raise ConfigurationError(
+ "You must specify the 'name' and 'layers' attribute.")
+
+ if name is not None:
+ interface = InterfaceClass(name, layers,
+ __doc__='Skin: %s' %name,
+ __module__='zope.app.skins')
+ # Add the layer to the skins module.
+ # Note: We have to do this immediately, so that directives using the
+ # InterfaceField can find the layer.
+ setattr(skins, name, interface)
+ path = 'zope.app.skins'+name
+ else:
+ path = name = interface.__module__ + '.' + interface.getName()
+
# Register the layer interface as an interface
- # Note: We have to do this immediately, so that directives using the
- # InterfaceField can find the layer.
_context.action(
- discriminator = ('interface', 'zope.app.skins.'+name),
+ discriminator = ('interface', path),
callable = provideInterface,
- args = ('zope.app.skins.'+name, skin),
+ args = (path, interface),
kw = {'info': _context.info}
)
@@ -94,11 +245,24 @@
_context.action(
discriminator = ('skin', name),
callable = provideInterface,
- args = (name, skin, ISkin, _context.info)
+ args = (name, interface, ISkin, _context.info)
)
-def setDefaultSkin(name, info):
- # XXX: Test
+def setDefaultSkin(name, info=''):
+ """Set the default skin.
+
+ >>> from zope.interface import directlyProvides
+ >>> from zope.app.tests import ztapi
+
+ >>> class Skin1(IBrowserRequest): pass
+ >>> directlyProvides(Skin1, ISkin)
+
+ >>> ztapi.provideUtility(ISkin, Skin1, 'Skin1')
+ >>> setDefaultSkin('Skin1')
+ >>> adapters = zapi.getService(zapi.servicenames.Adapters)
+ >>> adapters.lookup((IBrowserRequest,), IDefaultSkin, '') is Skin1
+ True
+ """
skin = zapi.getUtility(ISkin, name)
handler('Adapters', 'register',
(IBrowserRequest,), IDefaultSkin, '', skin, info),
Modified: Zope3/trunk/src/zope/app/publisher/browser/metadirectives.py
===================================================================
--- Zope3/trunk/src/zope/app/publisher/browser/metadirectives.py 2004-09-17 21:34:39 UTC (rev 27627)
+++ Zope3/trunk/src/zope/app/publisher/browser/metadirectives.py 2004-09-17 21:39:28 UTC (rev 27628)
@@ -478,14 +478,27 @@
class ILayerDirective(Interface):
"""Defines a browser layer
+
+ You must either specify a `name` or an `interface`. If you specify the
+ name, then a layer interface will be created for you based on the name and
+ the `base` interface.
+
+ If you do not specify a `base`, then `IBrowserRequest` is used by default.
+
+ You cannot specify both, the `interface` and the `base` attribute.
"""
name = TextLine(
title=u"Name",
description=u"The name of the layer.",
- required=True
+ required=False
)
+ interface = GlobalObject(
+ title=u"The layer's interface.",
+ required=False
+ )
+
base = GlobalObject(
title=u"Name",
description=u"The name of the skin",
@@ -494,14 +507,24 @@
class ISkinDirective(Interface):
"""Defines a browser skin
+
+ If you do not specify an `interface`, then one will be automatically
+ created for you based on the name using the layers as base interfaces.
+
+ You cannot specify both, the `interface` and the `layers` attribute.
"""
name = TextLine(
title=u"Name",
description=u"The name of the skin",
- required=True
+ required=False
)
+ interface = GlobalObject(
+ title=u"The skin's interface.",
+ required=False
+ )
+
layers = Tokens(
title=u"A list of layers",
description=u"""
@@ -509,6 +532,7 @@
has the same name as the skin, and the last skin should be
'default', unless you want to completely override all views.
""",
+ required=False,
value_type=LayerField()
)
Modified: Zope3/trunk/src/zope/app/publisher/browser/tests/test_directives.py
===================================================================
--- Zope3/trunk/src/zope/app/publisher/browser/tests/test_directives.py 2004-09-17 21:34:39 UTC (rev 27627)
+++ Zope3/trunk/src/zope/app/publisher/browser/tests/test_directives.py 2004-09-17 21:39:28 UTC (rev 27628)
@@ -24,10 +24,12 @@
from zope.configuration.xmlconfig import xmlconfig, XMLConfig
from zope.configuration.exceptions import ConfigurationError
from zope.app.component.tests.views import IC, V1, VZMI, R1, IV
+from zope.app.tests import placelesssetup
from zope.app.tests.placelesssetup import PlacelessSetup
from zope.security.proxy import ProxyFactory
import zope.security.management
from zope.security.proxy import removeSecurityProxy
+from zope.testing.doctestunit import DocTestSuite
from zope.app.publisher.browser.globalbrowsermenuservice import \
globalBrowserMenuService
@@ -1034,7 +1036,12 @@
self.assert_(isinstance(v, V1))
def test_suite():
- return unittest.makeSuite(Test)
+ return unittest.TestSuite((
+ unittest.makeSuite(Test),
+ DocTestSuite('zope.app.publisher.browser.metaconfigure',
+ setUp=placelesssetup.setUp,
+ tearDown=placelesssetup.tearDown)
+ ))
if __name__=='__main__':
unittest.main(defaultTest="test_suite")
More information about the Zope3-Checkins
mailing list