[Checkins] SVN: zope.publisher/trunk/ Make the default skin apply pattern adaptable for other

Roger Ineichen roger at projekt01.ch
Wed Mar 4 19:47:39 EST 2009


Log message for revision 97505:
  Make the default skin apply pattern adaptable for other
  requests which are not based on IBrowserRequest.
  A skin must provide ISkinType (IBrowserSkinType does that)
  and a request must provide ISkinnable for apply a skin.
  This is 100% compatible with the previous implementation
  since the default adapter is configured in zcml (grok?)
  
  - Fix: ensure that we only apply skin interface in setDefaultSkin which also
    provide IBrowserSkinType. This will ensure that we find a skin if the
    applySkin method will lookup for a skin based on this type interface.
  
  - Fix: Make it possible to use adapters and not only interfaces as skins from
    the adapter registry. Right now the defaultSkin directive registers simple
    interfaces as skin adapters which will run into a TypeError if someone tries
    to adapter such a skin adapter. Probably we should change the defaultSkin
    directive and register real adapters instead of using the interfaces as fake
    adapters where we expect adapter factories.
  
  - Feature: allow to use applySkin with different skin types using the optional
    argument skinType which is by default set to IBrowserSkinType
  
  - Feature: implemented the default skin pattern within adapters. This allows
    us to register default skins for other requests then only IBrowserRequest
    using IDefaultSkin adapters.
    
    Note, ISkinnable and ISkinType and the skin implementation should be moved
    out of the browser request modules. Packages like z3c.jsonrpc do not depend
    on IBrowserRequest but they are skinnable.
  
  - update tests

Changed:
  U   zope.publisher/trunk/CHANGES.txt
  U   zope.publisher/trunk/src/zope/publisher/browser.py
  U   zope.publisher/trunk/src/zope/publisher/configure.zcml
  U   zope.publisher/trunk/src/zope/publisher/interfaces/browser.py
  U   zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py

-=-
Modified: zope.publisher/trunk/CHANGES.txt
===================================================================
--- zope.publisher/trunk/CHANGES.txt	2009-03-05 00:40:18 UTC (rev 97504)
+++ zope.publisher/trunk/CHANGES.txt	2009-03-05 00:47:39 UTC (rev 97505)
@@ -4,6 +4,28 @@
 3.5.7dev (unreleased)
 ---------------------
 
+- Fix: ensure that we only apply skin interface in setDefaultSkin which also
+  provide IBrowserSkinType. This will ensure that we find a skin if the
+  applySkin method will lookup for a skin based on this type interface.
+
+- Fix: Make it possible to use adapters and not only interfaces as skins from
+  the adapter registry. Right now the defaultSkin directive registers simple
+  interfaces as skin adapters which will run into a TypeError if someone tries
+  to adapter such a skin adapter. Probably we should change the defaultSkin
+  directive and register real adapters instead of using the interfaces as fake
+  adapters where we expect adapter factories.
+
+- Feature: allow to use applySkin with different skin types using the optional
+  argument skinType which is by default set to IBrowserSkinType
+
+- Feature: implemented the default skin pattern within adapters. This allows
+  us to register default skins for other requests then only IBrowserRequest
+  using IDefaultSkin adapters.
+  
+  Note, ISkinnable and ISkinType and the skin implementation should be moved
+  out of the browser request modules. Packages like z3c.jsonrpc do not depend
+  on IBrowserRequest but they are skinnable.
+
 - Feature: added ISkinnable interface which allows us to implement the apply
   skin pattern not only for IBrowserRequest
 

Modified: zope.publisher/trunk/src/zope/publisher/browser.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/browser.py	2009-03-05 00:40:18 UTC (rev 97504)
+++ zope.publisher/trunk/src/zope/publisher/browser.py	2009-03-05 00:47:39 UTC (rev 97505)
@@ -28,8 +28,10 @@
 import tempfile
 
 import zope.component
-from zope.interface import implements, directlyProvides
+import zope.interface
+from zope.interface import implements, directlyProvides, alsoProvides
 from zope.interface import directlyProvidedBy, providedBy
+from zope.interface.interfaces import IInterface
 from zope.i18n.interfaces import IUserPreferredLanguages
 from zope.i18n.interfaces import IUserPreferredCharsets
 from zope.location import Location
@@ -41,6 +43,7 @@
 from zope.publisher.interfaces.browser import IBrowserApplicationRequest
 from zope.publisher.interfaces.browser import IBrowserView
 from zope.publisher.interfaces.browser import IBrowserPage
+from zope.publisher.interfaces.browser import ISkinType
 from zope.publisher.interfaces.browser import IBrowserSkinType
 from zope.publisher.interfaces.browser import ISkinChangedEvent
 from zope.publisher.interfaces.http import IHTTPRequest
@@ -907,15 +910,42 @@
         raise NotImplementedError("Subclasses should override __call__ to "
                                   "provide a response body")
 
+
+def getDefaultSkin(request):
+    """Returns the IDefaultSkin layer for IBrowserRequest."""
+    return IDefaultBrowserLayer
+
+
 def setDefaultSkin(request):
     """Sets the default skin for the request.
 
     The default skin is a marker interface that can be registered as an
-    adapter that provides IDefaultSkin for the request type.
+    adapter that provides IDefaultSkin for the request type. A default skin
+    interface like any other skin must also provide IBrowserSkinType. This is
+    important since applySkin will lookup for skins based on this type.
 
-    If a default skin is not available, the default layer
-    (IDefaultBrowserLayer) is used.
+    Note: Any interfaces that are directly provided by the request coming into
+    this method are replaced by the applied layer/skin interface. This is very
+    important since the retry pattern can use a clean request without any
+    directly provided interface.
 
+    If a default skin is not available, the fallback default skin get applied
+    if available for the given request type. The default fallback skin is
+    implemented as an named adapter factory providing IDefaultSkin and
+    using ``default`` as name. 
+    
+    Important to know is that some skin adapters get registered as interfaces
+    and the fallback skins as adapters. See the defaultSkin directive in 
+    zope.app.publication.zcml for more information which registers plain
+    interfaces as adapters which are not adaptable. (issue?)
+
+    Each request can only have one (unnamed) default skin and will fallback to
+    the named (default) fallback skin if available.
+
+    Only the IBrowserRequest provides such a default fallback adapter. This
+    adapter will apply the IDefaultBrowserLayer if no explicit default skin
+    is registered.
+
     To illustrate, we'll first use setDefaultSkin without a registered
     IDefaultSkin adapter:
 
@@ -926,11 +956,33 @@
       >>> IDefaultBrowserLayer.providedBy(request)
       False
 
+    If we try to set a default skin and no one exist we will not fail but
+    nothing happens
+
       >>> setDefaultSkin(request)
+
+    Make sure our IDefaultBrowserLayer provides the IBrowserSkinType interface.
+    This is done in the configure.zcml using the interface directive:
+    
+      >>> IBrowserSkinType.providedBy(IDefaultBrowserLayer)
+      False
+
+      >>> alsoProvides(IDefaultBrowserLayer, IBrowserSkinType)
+      >>> IBrowserSkinType.providedBy(IDefaultBrowserLayer)
+      True
+
+    The getDefaultSkin provides an adapter providing IDefaultSkin which we
+    register as named adapter using ``default`` as name:
+    
+      >>> zope.component.provideAdapter(getDefaultSkin,
+      ...     (IBrowserRequest,), IDefaultSkin, name='default')
+
+      >>> setDefaultSkin(request)
       >>> IDefaultBrowserLayer.providedBy(request)
       True
 
-    When we register a default layer, however:
+    When we register a default layer, wihtout that the skin provides an
+    ISkinType the skin doesn't get applied:
 
       >>> from zope.interface import Interface
       >>> class IMySkin(Interface):
@@ -938,12 +990,24 @@
       >>> zope.component.provideAdapter(IMySkin, (IBrowserRequest,),
       ...                               IDefaultSkin)
 
-    setDefaultSkin uses the layer instead of IDefaultBrowserLayer.providedBy:
+      >>> setDefaultSkin(request)
+      >>> IMySkin.providedBy(request)
+      False
+      >>> IDefaultBrowserLayer.providedBy(request)
+      True
 
+    The default skin must provide IBrowserSkinType:
+    
+      >>> alsoProvides(IMySkin, IBrowserSkinType)
+      >>> IBrowserSkinType.providedBy(IMySkin)
+      True
+
+    setDefaultSkin uses the layer instead of IDefaultBrowserLayer:
+
       >>> request = Request()
       >>> IMySkin.providedBy(request)
       False
-      >>> IDefaultSkin.providedBy(request)
+      >>> IDefaultBrowserLayer.providedBy(request)
       False
 
       >>> setDefaultSkin(request)
@@ -954,7 +1018,8 @@
       False
 
     Any interfaces that are directly provided by the request coming into this
-    method are replaced by the applied layer/skin interface:
+    method are replaced by the applied layer/skin interface. This is important
+    for our retry pattern which will ensure that we start with a clean request:
 
       >>> request = Request()
       >>> class IFoo(Interface):
@@ -969,12 +1034,24 @@
     """
     adapters = zope.component.getSiteManager().adapters
     skin = adapters.lookup((providedBy(request),), IDefaultSkin, '')
+    if skin is None:
+        # find a named ``default`` adapter providing IDefaultSkin as fallback
+        skin = adapters.lookup((providedBy(request),), IDefaultSkin,
+            'default')
     if skin is not None:
-        directlyProvides(request, skin)
-    else:
-        directlyProvides(request, IDefaultBrowserLayer)
+        try:
+            # the default fallback skin is registered as a named adapter
+            skin = skin(request)
+        except TypeError, e:
+            # the defaultSkin directive registers skins as interfaces and not
+            # as adapters (issue?)
+            pass
+        if ISkinType.providedBy(skin):
+            # silently ignore skins which do not provide ISkinType
+            directlyProvides(request, skin)
 
-def applySkin(request, skin):
+
+def applySkin(request, skin, skinType=IBrowserSkinType):
     """Change the presentation skin for this request.
 
     >>> import pprint
@@ -1016,9 +1093,10 @@
     >>> cleanUp()
 
     """
-    # Remove all existing skin declarations (commonly the default skin).
+    # Remove all existing skin declarations (commonly the default skin) based
+    # on the given skin type.
     ifaces = [iface for iface in directlyProvidedBy(request)
-              if not IBrowserSkinType.providedBy(iface)]
+              if not skinType.providedBy(iface)]
     # Add the new skin.
     ifaces.append(skin)
     directlyProvides(request, *ifaces)

Modified: zope.publisher/trunk/src/zope/publisher/configure.zcml
===================================================================
--- zope.publisher/trunk/src/zope/publisher/configure.zcml	2009-03-05 00:40:18 UTC (rev 97504)
+++ zope.publisher/trunk/src/zope/publisher/configure.zcml	2009-03-05 00:47:39 UTC (rev 97505)
@@ -6,6 +6,11 @@
 
   <interface interface="zope.publisher.interfaces.browser.IBrowserSkinType" />
   <interface interface="zope.publisher.interfaces.xmlrpc.IXMLRPCRequest" />
+
+  <interface
+      interface="zope.publisher.interfaces.browser.IDefaultBrowserLayer"
+      type="zope.publisher.interfaces.browser.IBrowserSkinType"
+      />
   
   <class class="xmlrpclib.Binary">
     <allow attributes="data encode decode" />
@@ -19,6 +24,13 @@
   <adapter factory=".xmlrpc.PythonDateTimePreMarshaller" />
   <adapter factory=".xmlrpc.DictPreMarshaller" />
 
+  <adapter
+      name="default"
+      factory=".browser.getDefaultSkin"
+      for="zope.publisher.interfaces.browser.IBrowserRequest"
+      provides="zope.publisher.interfaces.browser.IDefaultSkin"
+      />
+
   <apidoc:bookchapter
       zcml:condition="have apidoc"
       id="zopepublisherhttpresults.txt"

Modified: zope.publisher/trunk/src/zope/publisher/interfaces/browser.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/interfaces/browser.py	2009-03-05 00:40:18 UTC (rev 97504)
+++ zope.publisher/trunk/src/zope/publisher/interfaces/browser.py	2009-03-05 00:47:39 UTC (rev 97505)
@@ -18,7 +18,7 @@
 
 __docformat__ = "reStructuredText"
 
-from zope.interface import Interface, Attribute, directlyProvides
+from zope.interface import Interface, Attribute, directlyProvides, alsoProvides
 from zope.interface.interfaces import IInterface
 from zope.component.interfaces import IView
 
@@ -140,5 +140,8 @@
 class IDefaultBrowserLayer(IBrowserRequest):
     """The default layer."""
 
-class IBrowserSkinType(IInterface):
+class ISkinType(IInterface):
+    """Base interface for skin types."""
+
+class IBrowserSkinType(ISkinType):
     """A skin is a set of layers."""

Modified: zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py	2009-03-05 00:40:18 UTC (rev 97504)
+++ zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py	2009-03-05 00:47:39 UTC (rev 97505)
@@ -94,13 +94,16 @@
     def test_retry_keeps_everything(self):
         """lowlevel test for retry (see #98440)"""
         from zope.publisher.browser import TestRequest, setDefaultSkin
-        from zope.publisher.interfaces.browser import IDefaultSkin, IBrowserRequest
+        from zope.publisher.interfaces.browser import IDefaultSkin
+        from zope.publisher.interfaces.browser import IBrowserRequest
+        from zope.publisher.interfaces.browser import IBrowserSkinType
         # create a retryable request
         request = TestRequest()
         self.assertTrue(request.supportsRetry())
         # create a skin and register it as the default skin
         class ISomeSkin(Interface):
             pass
+        alsoProvides(ISomeSkin, IBrowserSkinType)
         provideAdapter(ISomeSkin, (IBrowserRequest,), IDefaultSkin)
         # set the default skin for the request
         setDefaultSkin(request)



More information about the Checkins mailing list