[Checkins] SVN: zope.publisher/trunk/ - Move skin related code from zope.publisher.interfaces.browser and
Roger Ineichen
roger at projekt01.ch
Sun Mar 8 17:27:26 EDT 2009
Log message for revision 97670:
- Move skin related code from zope.publisher.interfaces.browser and
zope.publisher.browser to zope.publihser.interfaces and
zope.publisher.skinnable and provide BBB imports.
- added tests for the skin concept
See skinnable.txt for more information.
Changed:
U zope.publisher/trunk/CHANGES.txt
U zope.publisher/trunk/setup.py
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/http.py
U zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py
U zope.publisher/trunk/src/zope/publisher/interfaces/browser.py
A zope.publisher/trunk/src/zope/publisher/skinnable.py
A zope.publisher/trunk/src/zope/publisher/skinnable.txt
U zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py
U zope.publisher/trunk/src/zope/publisher/tests/test_browserrequest.py
A zope.publisher/trunk/src/zope/publisher/tests/test_skinnable.py
-=-
Modified: zope.publisher/trunk/CHANGES.txt
===================================================================
--- zope.publisher/trunk/CHANGES.txt 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/CHANGES.txt 2009-03-08 21:27:26 UTC (rev 97670)
@@ -1,9 +1,14 @@
CHANGES
=======
-3.5.7dev (unreleased)
----------------------
+3.6.0 (unreleased)
+------------------
+- Clean-up: Move skin related code from zope.publisher.interfaces.browser and
+ zope.publisher.browser to zope.publihser.interfaces and
+ zope.publisher.skinnable and provide BBB imports. See skinnable.txt for more
+ information.
+
- 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.
Modified: zope.publisher/trunk/setup.py
===================================================================
--- zope.publisher/trunk/setup.py 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/setup.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -24,7 +24,7 @@
"""
setup(name='zope.publisher',
- version='3.5.7dev',
+ version='3.6.0dev',
url='http://pypi.python.org/pypi/zope.publisher',
license='ZPL 2.1',
author='Zope Corporation and Contributors',
Modified: zope.publisher/trunk/src/zope/publisher/browser.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/browser.py 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/src/zope/publisher/browser.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -37,18 +37,24 @@
from zope.location import Location
from zope.publisher.interfaces import NotFound
+from zope.publisher.interfaces import IDefaultSkin
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
-from zope.publisher.interfaces.browser import IDefaultSkin
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
from zope.publisher.http import HTTPRequest, HTTPResponse
+# BBB imports, this compoennts get moved from this module
+from zope.publisher.interfaces import ISkinType #BBB import
+from zope.publisher.interfaces import ISkinChangedEvent #BBB import
+from zope.publisher.skinnable import setDefaultSkin #BBB import
+from zope.publisher.skinnable import applySkin #BBB import
+from zope.publisher.skinnable import SkinChangedEvent #BBB import
+
+
__ArrayTypes = (ListType, TupleType)
start_of_header_search=re.compile('(<head[^>]*>)', re.I).search
@@ -914,197 +920,3 @@
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. 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.
-
- 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:
-
- >>> class Request(object):
- ... implements(IBrowserRequest)
-
- >>> request = Request()
- >>> 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, wihtout that the skin provides an
- ISkinType the skin doesn't get applied:
-
- >>> from zope.interface import Interface
- >>> class IMySkin(Interface):
- ... pass
- >>> zope.component.provideAdapter(IMySkin, (IBrowserRequest,),
- ... IDefaultSkin)
-
- >>> 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
- >>> IDefaultBrowserLayer.providedBy(request)
- False
-
- >>> setDefaultSkin(request)
-
- >>> IMySkin.providedBy(request)
- True
- >>> IDefaultBrowserLayer.providedBy(request)
- False
-
- Any interfaces that are directly provided by the request coming into this
- 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):
- ... pass
- >>> directlyProvides(request, IFoo)
- >>> IFoo.providedBy(request)
- True
- >>> setDefaultSkin(request)
- >>> IFoo.providedBy(request)
- False
-
- """
- 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:
- 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, skinType=IBrowserSkinType):
- """Change the presentation skin for this request.
-
- >>> import pprint
- >>> from zope.interface import Interface
- >>> class SkinA(Interface): pass
- >>> directlyProvides(SkinA, IBrowserSkinType)
- >>> class SkinB(Interface): pass
- >>> directlyProvides(SkinB, IBrowserSkinType)
- >>> class IRequest(Interface): pass
-
- >>> class Request(object):
- ... implements(IRequest)
-
- >>> req = Request()
-
- >>> applySkin(req, SkinA)
- >>> pprint.pprint(list(providedBy(req).interfaces()))
- [<InterfaceClass zope.publisher.browser.SkinA>,
- <InterfaceClass zope.publisher.browser.IRequest>]
-
- >>> applySkin(req, SkinB)
- >>> pprint.pprint(list(providedBy(req).interfaces()))
- [<InterfaceClass zope.publisher.browser.SkinB>,
- <InterfaceClass zope.publisher.browser.IRequest>]
-
- Changing the skin on a request triggers the ISkinChanged event:
-
- >>> import zope.component
- >>> from zope.publisher.interfaces.browser import ISkinChangedEvent
- >>> def receiveSkinEvent(event):
- ... print event.request
- >>> zope.component.provideHandler(receiveSkinEvent, (ISkinChangedEvent,))
- >>> applySkin(req, SkinA) # doctest: +ELLIPSIS
- <zope.publisher.browser.Request object at 0x...>
-
- Make sure our registrations go away again.
-
- >>> from zope.testing.cleanup import cleanUp
- >>> cleanUp()
-
- """
- # Remove all existing skin declarations (commonly the default skin) based
- # on the given skin type.
- ifaces = [iface for iface in directlyProvidedBy(request)
- if not skinType.providedBy(iface)]
- # Add the new skin.
- ifaces.append(skin)
- directlyProvides(request, *ifaces)
- zope.event.notify(SkinChangedEvent(request))
-
-class SkinChangedEvent(object):
-
- zope.interface.implements(ISkinChangedEvent)
-
- def __init__(self, request):
- self.request = request
Modified: zope.publisher/trunk/src/zope/publisher/configure.zcml
===================================================================
--- zope.publisher/trunk/src/zope/publisher/configure.zcml 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/src/zope/publisher/configure.zcml 2009-03-08 21:27:26 UTC (rev 97670)
@@ -28,7 +28,7 @@
name="default"
factory=".browser.getDefaultSkin"
for="zope.publisher.interfaces.browser.IBrowserRequest"
- provides="zope.publisher.interfaces.browser.IDefaultSkin"
+ provides="zope.publisher.interfaces.IDefaultSkin"
/>
<apidoc:bookchapter
Modified: zope.publisher/trunk/src/zope/publisher/http.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/http.py 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/src/zope/publisher/http.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -451,7 +451,7 @@
# restore the default skin
if IBrowserRequest.providedBy(self):
# only browser requests have skins
- zope.publisher.browser.setDefaultSkin(request)
+ zope.publisher.skinnable.setDefaultSkin(request)
request.setPublication(self.publication)
request._retry_count = self._retry_count
Modified: zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/src/zope/publisher/interfaces/__init__.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -17,10 +17,12 @@
"""
__docformat__ = "reStructuredText"
from zope.interface import Interface, Attribute, implements
+from zope.interface.interfaces import IInterface
from zope.interface.common.mapping import IEnumerableMapping
from zope.interface.common.interfaces import IException, ILookupError
from zope.security.interfaces import Unauthorized, IParticipation
+
class IPublishingException(IException):
pass
@@ -457,6 +459,33 @@
"""The basic request contract
"""
+
+class ISkinType(IInterface):
+ """Base interface for skin types."""
+
+
+class ISkinnable(Interface):
+ """A skinnable (request) can provide a skin.
+
+ The implementation in BrowserRequest will apply a default skin/layer called
+ ``IDefaultBrowserLayer`` if not default skin get registered.
+ """
+
+
+class IDefaultSkin(Interface):
+ """Any component providing this interface must be a skin.
+
+ This is a marker interface, so that we can register the default skin as an
+ adapter from the presentation type to `IDefaultSkin`.
+ """
+
+
+class ISkinChangedEvent(Interface):
+ """Event that gets triggered when the skin of a request is changed."""
+
+ request = Attribute("The request for which the skin was changed.")
+
+
class IView(Interface):
"""Generic view contract"""
Modified: zope.publisher/trunk/src/zope/publisher/interfaces/browser.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/interfaces/browser.py 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/src/zope/publisher/interfaces/browser.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -23,10 +23,18 @@
from zope.publisher.interfaces import IPublication
from zope.publisher.interfaces import IPublishTraverse
+from zope.publisher.interfaces import ISkinType
+from zope.publisher.interfaces import ISkinnable
from zope.publisher.interfaces import IView
from zope.publisher.interfaces.http import IHTTPApplicationRequest
from zope.publisher.interfaces.http import IHTTPRequest
+# BBB moved to zope.publisher.interfaces since not only browser reuquest
+# can use the skin pattern
+from zope.publisher.interfaces import IDefaultSkin # BBB import
+from zope.publisher.interfaces import ISkinChangedEvent # BBB import
+
+
class IBrowserApplicationRequest(IHTTPApplicationRequest):
"""Browser-specific requests
"""
@@ -74,28 +82,6 @@
"""
-class ISkinnable(Interface):
- """A skinnable (request) can provide a skin.
-
- The implementation in BrowserRequest will apply a default skin/layer called
- ``IDefaultBrowserLayer`` if not default skin get registered.
- """
-
-
-class IDefaultSkin(Interface):
- """Any component providing this interface must be a skin.
-
- This is a marker interface, so that we can register the default skin as an
- adapter from the presentation type to `IDefaultSkin`.
- """
-
-
-class ISkinChangedEvent(Interface):
- """Event that gets triggered when the skin of a request is changed."""
-
- request = Attribute("The request for which the skin was changed.")
-
-
class IBrowserRequest(IHTTPRequest, ISkinnable):
"""Browser-specific Request functionality.
@@ -142,8 +128,6 @@
class IDefaultBrowserLayer(IBrowserRequest):
"""The default layer."""
-class ISkinType(IInterface):
- """Base interface for skin types."""
class IBrowserSkinType(ISkinType):
"""A skin is a set of layers."""
Added: zope.publisher/trunk/src/zope/publisher/skinnable.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/skinnable.py (rev 0)
+++ zope.publisher/trunk/src/zope/publisher/skinnable.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Browser-specific Publisher classes
+
+Here we define the specific 'BrowserRequest' and 'BrowserResponse' class. The
+big improvement of the 'BrowserRequest' to 'HTTPRequest' is that is can handle
+HTML form data and convert them into a Python-native format. Even file data is
+packaged into a nice, Python-friendly 'FileUpload' object.
+
+$Id: browser.py 97505 2009-03-05 00:47:39Z rogerineichen $
+"""
+__docformat__ = 'restructuredtext'
+
+import zope.component
+import zope.interface
+
+from zope.publisher import interfaces
+
+
+class SkinChangedEvent(object):
+ """Skin changed event."""
+
+ zope.interface.implements(interfaces.ISkinChangedEvent)
+
+ def __init__(self, request):
+ self.request = request
+
+
+def setDefaultSkin(request):
+ """Sets the default skin for a given request."""
+ adapters = zope.component.getSiteManager().adapters
+ skin = adapters.lookup((zope.interface.providedBy(request),),
+ interfaces.IDefaultSkin, '')
+ if skin is None:
+ # find a named ``default`` adapter providing IDefaultSkin as fallback
+ skin = adapters.lookup((zope.interface.providedBy(request),),
+ interfaces.IDefaultSkin, 'default')
+ if skin is not None:
+ 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 interfaces.ISkinType.providedBy(skin):
+ # silently ignore skins which do not provide ISkinType
+ zope.interface.directlyProvides(request, skin)
+ else:
+ raise TypeError("Skin interface %s doesn't provide ISkinType" %
+ skin)
+
+
+def applySkin(request, skin):
+ """Change the presentation skin for this request."""
+ # Remove all existing skin type declarations (commonly the default skin)
+ # based on the given skin type.
+ ifaces = [iface for iface in zope.interface.directlyProvidedBy(request)
+ if not interfaces.ISkinType.providedBy(iface)]
+ # Add the new skin.
+ ifaces.append(skin)
+ zope.interface.directlyProvides(request, *ifaces)
+ zope.event.notify(SkinChangedEvent(request))
Added: zope.publisher/trunk/src/zope/publisher/skinnable.txt
===================================================================
--- zope.publisher/trunk/src/zope/publisher/skinnable.txt (rev 0)
+++ zope.publisher/trunk/src/zope/publisher/skinnable.txt 2009-03-08 21:27:26 UTC (rev 97670)
@@ -0,0 +1,304 @@
+=========
+Skinnable
+=========
+
+Request can provide skins. But what's exactly a skin. At the code level, a skin
+is just an interface which a request provides. Why do we need skins? We can use
+skins for register different adapter.
+
+That's a little bit much use of the word skin. Let's explain it mor detailed.
+A skin is an interface which provides a type interface. This type interface
+is called ISkinType. The zope.publisher right now provides only one specific
+skin type interface used in the IBrowserRequest implementation. This interface
+is called BrowserSkinType.
+
+Since the zope server provides request factories for biuld a request, each
+such request type could provide it's own skin type interface. This ensures that
+we can register a skin type for each request.
+
+Now a more high level point of view. A skin is a concept which we can use for
+provide different kind of views, templates or other adapter adapting a request.
+This skins are the key component for provide different kind of application
+layers. Then a skin makes it possible that an application can act very
+different with each skin. Of corse that's only the case at the interaction
+level where the request is involved. But that's moste the time the case since
+we have an web application server.
+
+Another part of the skinnable concept is that a skin can define a default skin.
+This is done within the IDefaultSkin interface. Such a default skin get defined
+at the level request implementation level. Such a default skin can get overriden
+in a custom setup. Overriding a skin can be done by using the defaultSkin
+directive offeren from zope.app.publication.zcml.
+
+Why does a request need a default skin. If a request needs to provide some
+pluggable concepts which requires that a default adapter is registered for
+a request, this adapter could be registered for the default skin. If a project
+likes to use another pattern and needs to register another request adapter, the
+project could register it's own skin and register the custom adapter for this
+new project based skin. This is very handy and allows to skip a complete
+default skin based setup for a given request.
+
+In general this means a request interface and the request class wich implements
+the request interface does only provide the basic API but no adapters if the
+request needs to delegate things to an adapter. For such a request a default
+skin can get defined. This default skin can provide all adatpers which the
+request implementation needs to have. This gives us to option to replace the
+default skin within an own skin and provide custom adapters.
+
+Our exmple will define a full request and all it's component from scratch.
+it doesn't depend on IBrowserRequest. We'll use a JSON-RPC as sample like
+the z3c.jsonrpc package provides.
+
+Layers and Skins
+----------------
+
+We also use the term layer if we talk about skins. A layer or skin layer is an
+interface registered as a ISkinType without a name. Zope provides a traversal
+pattern which allows to traverse a skin within a skin namespace called
+``skin``. This allows to traverse to traverse to a method called applySkin
+which will aplly a registered named skin. this means if we register a ISkinType
+as within an optional name argument, we will register a skin. if we register a
+ISkinType without a name just we register a layer. This means, layers are not
+traversable ISkinType interfaces.
+
+Let's start define a request:
+
+ >>> from zope.publisher.interfaces import IRequest
+ >>> class IJSONRequest(IRequest):
+ ... """JSON request."""
+
+And we define a skin type:
+
+ >>> from zope.publisher.interfaces import ISkinType
+ >>> class IJSONSkinType(ISkinType):
+ ... """JSON skin type."""
+
+A request would implement the IJSONRequest interface but not the request type
+interface:
+
+ >>> import zope.interface
+ >>> from zope.publisher.base import BaseRequest
+ >>> class JSONRequest(BaseRequest):
+ ... """JSON request implementation."""
+ ... zope.interface.implements(IJSONRequest)
+
+Now our request provides IJSONRequest because it implement that interface:
+
+ >>> from StringIO import StringIO
+ >>> request = JSONRequest(StringIO(''), {})
+ >>> IJSONRequest.providedBy(request)
+ True
+
+
+setDefaultSkin
+--------------
+
+The default skin is a marker interface that can be registered as an
+adapter that provides IDefaultSkin for the request type. A default skin
+interface like any other skin must also provide ISkinType. This is
+important since applySkin will lookup for skins based on this type.
+
+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 after a retry get started.
+
+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 for IBrowserRequest.
+
+Our test setup requires a custom default layer which we will apply to our
+request. Let's define a custm layer:
+
+ >>> class IJSONDefaultLayer(zope.interface.Interface):
+ ... """JSON default layyer."""
+
+To illustrate, we'll first use setDefaultSkin without a registered
+IDefaultSkin adapter:
+
+ >>> IJSONDefaultLayer.providedBy(request)
+ False
+
+If we try to set a default skin and no one exist we will not fail but
+nothing happens
+
+ >>> from zope.publisher.skinnable import setDefaultSkin
+ >>> setDefaultSkin(request)
+
+Make sure our IJSONDefaultLayer provides the ISkinType interface.
+This is normaly done in a configure.zcml using the interface directive:
+
+ >>> ISkinType.providedBy(IJSONDefaultLayer)
+ False
+
+ >>> zope.interface.alsoProvides(IJSONDefaultLayer, ISkinType)
+ >>> ISkinType.providedBy(IJSONDefaultLayer)
+ True
+
+Let's define a default skin adatper which the setDefaulSkin can use. This
+adapter return our IJSONDefaultLayer. We also register this adapter within
+``default`` as name:
+
+ >>> from zope.publisher.interfaces import IDefaultSkin
+ >>> def getDefaultJSONLayer(request):
+ ... return IJSONDefaultLayer
+
+ >>> zope.component.provideAdapter(getDefaultJSONLayer,
+ ... (IJSONRequest,), IDefaultSkin, name='default')
+
+ >>> setDefaultSkin(request)
+ >>> IJSONDefaultLayer.providedBy(request)
+ True
+
+When we register a default skin, without that the skin provides an ISkinType,
+the setDefaultSkin will raise a TypeError:
+
+
+ >>> from zope.interface import Interface
+ >>> class IMySkin(Interface):
+ ... pass
+ >>> zope.component.provideAdapter(IMySkin, (IJSONRequest,), IDefaultSkin)
+ >>> setDefaultSkin(request)
+ Traceback (most recent call last):
+ ...
+ TypeError: Skin interface <InterfaceClass __builtin__.IMySkin> doesn't provide ISkinType
+
+The default skin must provide ISkinType:
+
+ >>> zope.interface.alsoProvides(IMySkin, ISkinType)
+ >>> ISkinType.providedBy(IMySkin)
+ True
+
+setDefaultSkin uses the custom layer interface instead of IJSONDefaultLayer:
+
+ >>> request = JSONRequest(StringIO(''), {})
+ >>> IMySkin.providedBy(request)
+ False
+
+ >>> IJSONDefaultLayer.providedBy(request)
+ False
+
+ >>> setDefaultSkin(request)
+
+ >>> IMySkin.providedBy(request)
+ True
+
+ >>> IJSONDefaultLayer.providedBy(request)
+ False
+
+Any interfaces that are directly provided by the request coming into this
+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 = JSONRequest(StringIO(''), {})
+ >>> class IFoo(Interface):
+ ... pass
+
+ >>> zope.interface.directlyProvides(request, IFoo)
+ >>> IFoo.providedBy(request)
+ True
+
+ >>> setDefaultSkin(request)
+ >>> IFoo.providedBy(request)
+ False
+
+
+applySkin
+---------
+
+The applySkin method is able to apply any given skin. Let's define some custom
+skins:
+
+ >>> import pprint
+ >>> from zope.interface import Interface
+ >>> class ISkinA(Interface):
+ ... pass
+
+ >>> zope.interface.directlyProvides(ISkinA, ISkinType)
+ >>> class ISkinB(Interface):
+ ... pass
+
+ >>> zope.interface.directlyProvides(ISkinB, ISkinType)
+
+Let's start with a fresh request:
+
+ >>> request = JSONRequest(StringIO(''), {})
+
+Now we can apply the SkinA:
+
+ >>> from zope.publisher.skinnable import applySkin
+ >>> applySkin(request, ISkinA)
+ >>> pprint.pprint(list(zope.interface.providedBy(request).interfaces()))
+ [<InterfaceClass __builtin__.ISkinA>,
+ <InterfaceClass __builtin__.IJSONRequest>,
+ <InterfaceClass zope.publisher.interfaces.IRequest>]
+
+And if we apply ISkinB, ISkinA get removed at the same time ISkinB get applied:
+
+ >>> applySkin(request, ISkinB)
+ >>> pprint.pprint(list(zope.interface.providedBy(request).interfaces()))
+ [<InterfaceClass __builtin__.ISkinB>,
+ <InterfaceClass __builtin__.IJSONRequest>,
+ <InterfaceClass zope.publisher.interfaces.IRequest>]
+
+
+setDefaultSkin and applySkin
+----------------------------
+
+If we set a default skin and later apply a custom skin, the default skin get
+removed at the time the applySkin get called within a new ISkinType:
+
+ >>> request = JSONRequest(StringIO(''), {})
+
+Note, that our IMySkin is the default skin for IJSONRequest. We can aprove that
+by lookup an IDefaultSkin interface for our request:
+
+ >>> adapters = zope.component.getSiteManager().adapters
+ >>> default = adapters.lookup((zope.interface.providedBy(request),),
+ ... IDefaultSkin, '')
+ >>> default
+ <InterfaceClass __builtin__.IMySkin>
+
+ >>> setDefaultSkin(request)
+ >>> IMySkin.providedBy(request)
+ True
+
+ >>> ISkinA.providedBy(request)
+ False
+
+Now apply our skin ISkinA. This should remove the IMySkin at the same time the
+ISkinA get applied:
+
+ >>> applySkin(request, ISkinA)
+ >>> IMySkin.providedBy(request)
+ False
+
+ >>> ISkinA.providedBy(request)
+ True
+
+
+SkinChangedEvent
+----------------
+
+Changing the skin on a request triggers the ISkinChangedEvent event:
+
+ >>> import zope.component
+ >>> from zope.publisher.interfaces import ISkinChangedEvent
+ >>> def receiveSkinEvent(event):
+ ... print "Notified SkinEvent for:", event.request.__class__.__name__
+ >>> zope.component.provideHandler(receiveSkinEvent, (ISkinChangedEvent,))
+ >>> applySkin(request, ISkinA)
+ Notified SkinEvent for: JSONRequest
Modified: zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/src/zope/publisher/tests/test_baserequest.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -93,8 +93,9 @@
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
+ from zope.publisher.browser import TestRequest
+ from zope.publisher.skinnable import setDefaultSkin
+ from zope.publisher.interfaces import IDefaultSkin
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.publisher.interfaces.browser import IBrowserSkinType
# create a retryable request
Modified: zope.publisher/trunk/src/zope/publisher/tests/test_browserrequest.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/tests/test_browserrequest.py 2009-03-08 20:54:11 UTC (rev 97669)
+++ zope.publisher/trunk/src/zope/publisher/tests/test_browserrequest.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -23,12 +23,11 @@
from zope.publisher.http import HTTPCharsets
from zope.publisher.browser import BrowserRequest
from zope.publisher.interfaces import NotFound
-
-from zope.publisher.base import DefaultPublication
+from zope.publisher.interfaces import ISkinnable
from zope.publisher.interfaces.browser import IBrowserApplicationRequest
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.publisher.interfaces.browser import IBrowserPublication
-from zope.publisher.interfaces.browser import ISkinnable
+from zope.publisher.base import DefaultPublication
from zope.publisher.tests.test_http import HTTPTests
from zope.publisher.tests.publication import TestPublication
Added: zope.publisher/trunk/src/zope/publisher/tests/test_skinnable.py
===================================================================
--- zope.publisher/trunk/src/zope/publisher/tests/test_skinnable.py (rev 0)
+++ zope.publisher/trunk/src/zope/publisher/tests/test_skinnable.py 2009-03-08 21:27:26 UTC (rev 97670)
@@ -0,0 +1,35 @@
+# -*- coding: latin-1 -*-
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""HTTP Publisher Tests
+
+$Id: test_http.py 96537 2009-02-14 15:13:58Z benji_york $
+"""
+
+import unittest
+import zope.testing
+
+
+def cleanUp(test):
+ zope.testing.cleanup.cleanUp()
+
+
+def test_suite():
+ return unittest.TestSuite(
+ zope.testing.doctest.DocFileSuite('../skinnable.txt',
+ setUp=cleanUp, tearDown=cleanUp))
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
More information about the Checkins
mailing list