[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