[Zope3-checkins] SVN: Zope3/trunk/ Refactored zope.decorator into zope.proxy.decorator and zope.security.decorator

Wolfgang Schnerring wosc at wosc.de
Fri Sep 22 02:40:32 EDT 2006


Log message for revision 70320:
  Refactored zope.decorator into zope.proxy.decorator and zope.security.decorator
  as discussed in http://article.gmane.org/gmane.comp.web.zope.zope3/18586
  

Changed:
  U   Zope3/trunk/doc/CHANGES.txt
  D   Zope3/trunk/src/zope/decorator/DEPENDENCIES.cfg
  U   Zope3/trunk/src/zope/decorator/__init__.py
  D   Zope3/trunk/src/zope/decorator/tests.py
  U   Zope3/trunk/src/zope/location/location.py
  A   Zope3/trunk/src/zope/proxy/decorator.py
  A   Zope3/trunk/src/zope/proxy/tests/test_decorator.py
  A   Zope3/trunk/src/zope/security/decorator.py

-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/doc/CHANGES.txt	2006-09-22 06:40:31 UTC (rev 70320)
@@ -59,6 +59,9 @@
 
     Restructuring
 
+      - Refactored zope.decorator into zope.proxy.decorator and
+        zope.security.decorator.
+
       - Deprecated directive <zope:localUtility>, replaced by <zope:class>.
 
       - Removed deprecated directive <zope:defaultLayer>.

Deleted: Zope3/trunk/src/zope/decorator/DEPENDENCIES.cfg
===================================================================
--- Zope3/trunk/src/zope/decorator/DEPENDENCIES.cfg	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/src/zope/decorator/DEPENDENCIES.cfg	2006-09-22 06:40:31 UTC (rev 70320)
@@ -1,3 +0,0 @@
-zope.proxy
-zope.interface
-zope.security

Modified: Zope3/trunk/src/zope/decorator/__init__.py
===================================================================
--- Zope3/trunk/src/zope/decorator/__init__.py	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/src/zope/decorator/__init__.py	2006-09-22 06:40:31 UTC (rev 70320)
@@ -1,249 +1,11 @@
-##############################################################################
-#
-# Copyright (c) 2003 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.
-#
-##############################################################################
-"""Decorator support
+import zope.deprecation
+zope.deprecation.moved(
+    "zope.proxy.decorator",
+    "Zope 3.6",
+    )
+zope.deprecation.moved(
+    "zope.security.decorator",
+    "Zope 3.6",
+    )
 
-Decorators are proxies that are mostly transparent but that may provide
-additional features.
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-
-from zope.proxy import getProxiedObject, ProxyBase
-from zope.security.checker import selectChecker, CombinedChecker
-from zope.security.proxy import Proxy, getChecker
-from zope.interface.declarations import ObjectSpecificationDescriptor
-from zope.interface.declarations import getObjectSpecification
-from zope.interface.declarations import ObjectSpecification
-from zope.interface import providedBy
-
-class DecoratorSpecificationDescriptor(ObjectSpecificationDescriptor):
-    """Support for interface declarations on decorators
-
-    >>> from zope.interface import *
-    >>> class I1(Interface):
-    ...     pass
-    >>> class I2(Interface):
-    ...     pass
-    >>> class I3(Interface):
-    ...     pass
-    >>> class I4(Interface):
-    ...     pass
-
-    >>> class D1(Decorator):
-    ...   implements(I1)
-
-
-    >>> class D2(Decorator):
-    ...   implements(I2)
-
-    >>> class X(object):
-    ...   implements(I3)
-
-    >>> x = X()
-    >>> directlyProvides(x, I4)
-
-    Interfaces of X are ordered with the directly-provided interfaces first
-
-    >>> [interface.getName() for interface in list(providedBy(x))]
-    ['I4', 'I3']
-
-    When we decorate objects, what order should the interfaces come
-    in?  One could argue that decorators are less specific, so they
-    should come last.
-
-    >>> [interface.getName() for interface in list(providedBy(D1(x)))]
-    ['I4', 'I3', 'I1']
-
-    >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))]
-    ['I4', 'I3', 'I1', 'I2']
-    """
-    def __get__(self, inst, cls=None):
-        if inst is None:
-            return getObjectSpecification(cls)
-        else:
-            provided = providedBy(getProxiedObject(inst))
-
-            # Use type rather than __class__ because inst is a proxy and
-            # will return the proxied object's class.
-            cls = type(inst)
-            return ObjectSpecification(provided, cls)
-
-    def __set__(self, inst, value):
-        raise TypeError("Can't set __providedBy__ on a decorated object")
-
-class DecoratedSecurityCheckerDescriptor(object):
-    """Descriptor for a Decorator that provides a decorated security checker.
-
-    To illustrate, we'll create a class that will be proxied:
-
-      >>> class Foo(object):
-      ...     a = 'a'
-
-    and a class to proxy it that uses a decorated security checker:
-
-      >>> class Wrapper(ProxyBase):
-      ...     b = 'b'
-      ...     __Security_checker__ = DecoratedSecurityCheckerDescriptor()
-
-    Next we'll create and register a checker for `Foo`:
-
-      >>> from zope.security.checker import NamesChecker, defineChecker
-      >>> fooChecker = NamesChecker(['a'])
-      >>> defineChecker(Foo, fooChecker)
-
-    along with a checker for `Wrapper`:
-
-      >>> wrapperChecker = NamesChecker(['b'])
-      >>> defineChecker(Wrapper, wrapperChecker)
-
-    Using `selectChecker()`, we can confirm that a `Foo` object uses
-    `fooChecker`:
-
-      >>> foo = Foo()
-      >>> selectChecker(foo) is fooChecker
-      True
-      >>> fooChecker.check(foo, 'a')
-      >>> fooChecker.check(foo, 'b')  # doctest: +ELLIPSIS
-      Traceback (most recent call last):
-      ForbiddenAttribute: ('b', <zope.decorator.Foo object ...>)
-
-    and that a `Wrapper` object uses `wrappeChecker`:
-
-      >>> wrapper = Wrapper(foo)
-      >>> selectChecker(wrapper) is wrapperChecker
-      True
-      >>> wrapperChecker.check(wrapper, 'b')
-      >>> wrapperChecker.check(wrapper, 'a')  # doctest: +ELLIPSIS
-      Traceback (most recent call last):
-      ForbiddenAttribute: ('a', <zope.decorator.Foo object ...>)
-
-    (Note that the object description says `Foo` because the object is a
-    proxy and generally looks and acts like the object it's proxying.)
-
-    When we access wrapper's ``__Security_checker__`` attribute, we invoke
-    the decorated security checker descriptor. The decorator's job is to make
-    sure checkers from both objects are used when available. In this case,
-    because both objects have checkers, we get a combined checker:
-
-      >>> checker = wrapper.__Security_checker__
-      >>> type(checker)
-      <class 'zope.security.checker.CombinedChecker'>
-      >>> checker.check(wrapper, 'a')
-      >>> checker.check(wrapper, 'b')
-
-    The decorator checker will work even with security proxied objects. To
-    illustrate, we'll proxify `foo`:
-
-      >>> from zope.security.proxy import ProxyFactory
-      >>> secure_foo = ProxyFactory(foo)
-      >>> secure_foo.a
-      'a'
-      >>> secure_foo.b  # doctest: +ELLIPSIS
-      Traceback (most recent call last):
-      ForbiddenAttribute: ('b', <zope.decorator.Foo object ...>)
-
-    when we wrap the secured `foo`:
-
-      >>> wrapper = Wrapper(secure_foo)
-
-    we still get a combined checker:
-
-      >>> checker = wrapper.__Security_checker__
-      >>> type(checker)
-      <class 'zope.security.checker.CombinedChecker'>
-      >>> checker.check(wrapper, 'a')
-      >>> checker.check(wrapper, 'b')
-
-    The decorator checker has three other scenarios:
-
-      - the wrapper has a checker but the proxied object doesn't
-      - the proxied object has a checker but the wrapper doesn't
-      - neither the wrapper nor the proxied object have checkers
-
-    When the wrapper has a checker but the proxied object doesn't:
-
-      >>> from zope.security.checker import NoProxy, _checkers
-      >>> del _checkers[Foo]
-      >>> defineChecker(Foo, NoProxy)
-      >>> selectChecker(foo) is None
-      True
-      >>> selectChecker(wrapper) is wrapperChecker
-      True
-
-    the decorator uses only the wrapper checker:
-
-      >>> wrapper = Wrapper(foo)
-      >>> wrapper.__Security_checker__ is wrapperChecker
-      True
-
-    When the proxied object has a checker but the wrapper doesn't:
-
-      >>> del _checkers[Wrapper]
-      >>> defineChecker(Wrapper, NoProxy)
-      >>> selectChecker(wrapper) is None
-      True
-      >>> del _checkers[Foo]
-      >>> defineChecker(Foo, fooChecker)
-      >>> selectChecker(foo) is fooChecker
-      True
-
-    the decorator uses only the proxied object checker:
-
-      >>> wrapper.__Security_checker__ is fooChecker
-      True
-
-    Finally, if neither the wrapper not the proxied have checkers:
-
-      >>> del _checkers[Foo]
-      >>> defineChecker(Foo, NoProxy)
-      >>> selectChecker(foo) is None
-      True
-      >>> selectChecker(wrapper) is None
-      True
-
-    the decorator doesn't have a checker:
-
-      >>> wrapper.__Security_checker__ is None
-      True
-
-    """
-    def __get__(self, inst, cls=None):
-        if inst is None:
-            return self
-        else:
-            proxied_object = getProxiedObject(inst)
-            if type(proxied_object) is Proxy:
-                checker = getChecker(proxied_object)
-            else:
-                checker = getattr(proxied_object, '__Security_checker__', None)
-                if checker is None:
-                    checker = selectChecker(proxied_object)
-            wrapper_checker = selectChecker(inst)
-            if wrapper_checker is None:
-                return checker
-            elif checker is None:
-                return wrapper_checker
-            else:
-                return CombinedChecker(wrapper_checker, checker)
-
-    def __set__(self, inst, value):
-        raise TypeError("Can't set __Security_checker__ on a decorated object")
-
-class Decorator(ProxyBase):
-    """Decorator base class"""
-
-    __providedBy__ = DecoratorSpecificationDescriptor()
-    __Security_checker__ = DecoratedSecurityCheckerDescriptor()
-
+Decorator = DecoratorBase

Deleted: Zope3/trunk/src/zope/decorator/tests.py
===================================================================
--- Zope3/trunk/src/zope/decorator/tests.py	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/src/zope/decorator/tests.py	2006-09-22 06:40:31 UTC (rev 70320)
@@ -1,92 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 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.
-#
-##############################################################################
-"""Context Tests
-
-$Id$
-"""
-import pickle
-import unittest
-from zope.decorator import Decorator
-from zope.interface import Interface, implements, directlyProvides, providedBy
-from zope.interface import directlyProvidedBy, implementedBy
-from zope.testing.doctestunit import DocTestSuite
-from zope.security.interfaces import ForbiddenAttribute
-
-class I1(Interface):
-    pass
-class I2(Interface):
-    pass
-class I3(Interface):
-    pass
-class I4(Interface):
-    pass
-
-class D1(Decorator):
-  implements(I1)
-
-class D2(Decorator):
-  implements(I2)
-
-
-def check_forbidden_call(callable, *args):
-    try:
-        return callable(*args)
-    except ForbiddenAttribute, e:
-        return 'ForbiddenAttribute: %s' % e[0]
-
-
-def test_providedBy_iter_w_new_style_class():
-    """
-    >>> class X(object):
-    ...   implements(I3)
-
-    >>> x = X()
-    >>> directlyProvides(x, I4)
-
-    >>> [interface.getName() for interface in list(providedBy(x))]
-    ['I4', 'I3']
-
-    >>> [interface.getName() for interface in list(providedBy(D1(x)))]
-    ['I4', 'I3', 'I1']
-
-    >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))]
-    ['I4', 'I3', 'I1', 'I2']
-    """
-
-def test_providedBy_iter_w_classic_class():
-    """
-    >>> class X(object):
-    ...   implements(I3)
-
-    >>> x = X()
-    >>> directlyProvides(x, I4)
-
-    >>> [interface.getName() for interface in list(providedBy(x))]
-    ['I4', 'I3']
-
-    >>> [interface.getName() for interface in list(providedBy(D1(x)))]
-    ['I4', 'I3', 'I1']
-
-    >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))]
-    ['I4', 'I3', 'I1', 'I2']
-    """
-
-def test_suite():
-    suite = DocTestSuite()
-    suite.addTest(DocTestSuite('zope.decorator'))
-    return suite
-
-
-if __name__ == '__main__':
-    unittest.main()

Modified: Zope3/trunk/src/zope/location/location.py
===================================================================
--- Zope3/trunk/src/zope/location/location.py	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/src/zope/location/location.py	2006-09-22 06:40:31 UTC (rev 70320)
@@ -20,8 +20,7 @@
 import zope.interface
 from zope.location.interfaces import ILocation
 from zope.proxy import ProxyBase, getProxiedObject, non_overridable
-from zope.decorator import DecoratorSpecificationDescriptor
-from zope.decorator import DecoratedSecurityCheckerDescriptor
+from zope.security.decorator import DecoratorBase
 
 class Location(object):
     """Stupid mix-in that defines `__parent__` and `__name__` attributes
@@ -120,7 +119,7 @@
             return self.funcs[1](cls)
         return self.funcs[0](inst)
 
-class LocationProxy(ProxyBase):
+class LocationProxy(DecoratorBase):
     __doc__ = """Location-object proxy
 
     This is a non-picklable proxy that can be put around objects that
@@ -172,7 +171,3 @@
         )
     
     __reduce_ex__ = __reduce__
-
-    __providedBy__ = DecoratorSpecificationDescriptor()
-
-    __Security_checker__ = DecoratedSecurityCheckerDescriptor()

Added: Zope3/trunk/src/zope/proxy/decorator.py
===================================================================
--- Zope3/trunk/src/zope/proxy/decorator.py	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/src/zope/proxy/decorator.py	2006-09-22 06:40:31 UTC (rev 70320)
@@ -0,0 +1,108 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Decorator support
+
+Decorators are proxies that are mostly transparent but that may provide
+additional features.
+
+$Id: __init__.py 66343 2006-04-03 04:59:49Z philikon $
+"""
+__docformat__ = "reStructuredText"
+
+from zope.proxy import getProxiedObject, ProxyBase
+from zope.security.checker import selectChecker, CombinedChecker
+from zope.security.proxy import Proxy, getChecker
+from zope.interface.declarations import ObjectSpecificationDescriptor
+from zope.interface.declarations import getObjectSpecification
+from zope.interface.declarations import ObjectSpecification
+from zope.interface import providedBy
+
+class DecoratorSpecificationDescriptor(ObjectSpecificationDescriptor):
+    """Support for interface declarations on decorators
+
+    >>> from zope.interface import *
+    >>> class I1(Interface):
+    ...     pass
+    >>> class I2(Interface):
+    ...     pass
+    >>> class I3(Interface):
+    ...     pass
+    >>> class I4(Interface):
+    ...     pass
+
+    >>> class D1(SpecificationDecoratorBase):
+    ...   implements(I1)
+
+
+    >>> class D2(SpecificationDecoratorBase):
+    ...   implements(I2)
+
+    >>> class X(object):
+    ...   implements(I3)
+
+    >>> x = X()
+    >>> directlyProvides(x, I4)
+
+    Interfaces of X are ordered with the directly-provided interfaces first
+
+    >>> [interface.getName() for interface in list(providedBy(x))]
+    ['I4', 'I3']
+
+    When we decorate objects, what order should the interfaces come
+    in?  One could argue that decorators are less specific, so they
+    should come last.
+
+    >>> [interface.getName() for interface in list(providedBy(D1(x)))]
+    ['I4', 'I3', 'I1']
+
+    >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))]
+    ['I4', 'I3', 'I1', 'I2']
+
+    SpecificationDecorators also work with old-style classes:
+
+    >>> class X:
+    ...   implements(I3)
+
+    >>> x = X()
+    >>> directlyProvides(x, I4)
+
+    >>> [interface.getName() for interface in list(providedBy(x))]
+    ['I4', 'I3']
+
+    >>> [interface.getName() for interface in list(providedBy(D1(x)))]
+    ['I4', 'I3', 'I1']
+
+    >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))]
+    ['I4', 'I3', 'I1', 'I2']
+    """
+    def __get__(self, inst, cls=None):
+        if inst is None:
+            return getObjectSpecification(cls)
+        else:
+            provided = providedBy(getProxiedObject(inst))
+
+            # Use type rather than __class__ because inst is a proxy and
+            # will return the proxied object's class.
+            cls = type(inst)
+            return ObjectSpecification(provided, cls)
+
+    def __set__(self, inst, value):
+        raise TypeError("Can't set __providedBy__ on a decorated object")
+
+
+class SpecificationDecoratorBase(ProxyBase):
+    """Base class for a proxy that provides additional interfaces."""
+
+    __providedBy__ = DecoratorSpecificationDescriptor()
+

Added: Zope3/trunk/src/zope/proxy/tests/test_decorator.py
===================================================================
--- Zope3/trunk/src/zope/proxy/tests/test_decorator.py	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/src/zope/proxy/tests/test_decorator.py	2006-09-22 06:40:31 UTC (rev 70320)
@@ -0,0 +1,24 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Test Harness
+
+$Id: tests.py 66343 2006-04-03 04:59:49Z philikon $
+"""
+from zope.testing.doctestunit import DocTestSuite
+
+
+def test_suite():
+    suite = DocTestSuite()
+    suite.addTest(DocTestSuite('zope.proxy.decorator'))
+    return suite

Added: Zope3/trunk/src/zope/security/decorator.py
===================================================================
--- Zope3/trunk/src/zope/security/decorator.py	2006-09-22 05:29:49 UTC (rev 70319)
+++ Zope3/trunk/src/zope/security/decorator.py	2006-09-22 06:40:31 UTC (rev 70320)
@@ -0,0 +1,201 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Decorator support
+
+Decorators are proxies that are mostly transparent but that may provide
+additional features.
+
+$Id: __init__.py 66343 2006-04-03 04:59:49Z philikon $
+"""
+__docformat__ = "reStructuredText"
+
+from zope.proxy import getProxiedObject, ProxyBase
+from zope.proxy.decorator import SpecificationDecoratorBase
+from zope.security.checker import selectChecker, CombinedChecker
+from zope.security.proxy import Proxy, getChecker
+from zope.interface.declarations import ObjectSpecificationDescriptor
+from zope.interface.declarations import getObjectSpecification
+from zope.interface.declarations import ObjectSpecification
+from zope.interface import providedBy
+
+class DecoratedSecurityCheckerDescriptor(object):
+    """Descriptor for a Decorator that provides a decorated security checker.
+
+    To illustrate, we'll create a class that will be proxied:
+
+      >>> class Foo(object):
+      ...     a = 'a'
+
+    and a class to proxy it that uses a decorated security checker:
+
+      >>> class Wrapper(ProxyBase):
+      ...     b = 'b'
+      ...     __Security_checker__ = DecoratedSecurityCheckerDescriptor()
+
+    Next we'll create and register a checker for `Foo`:
+
+      >>> from zope.security.checker import NamesChecker, defineChecker
+      >>> fooChecker = NamesChecker(['a'])
+      >>> defineChecker(Foo, fooChecker)
+
+    along with a checker for `Wrapper`:
+
+      >>> wrapperChecker = NamesChecker(['b'])
+      >>> defineChecker(Wrapper, wrapperChecker)
+
+    Using `selectChecker()`, we can confirm that a `Foo` object uses
+    `fooChecker`:
+
+      >>> foo = Foo()
+      >>> selectChecker(foo) is fooChecker
+      True
+      >>> fooChecker.check(foo, 'a')
+      >>> fooChecker.check(foo, 'b')  # doctest: +ELLIPSIS
+      Traceback (most recent call last):
+      ForbiddenAttribute: ('b', <zope.decorator.Foo object ...>)
+
+    and that a `Wrapper` object uses `wrappeChecker`:
+
+      >>> wrapper = Wrapper(foo)
+      >>> selectChecker(wrapper) is wrapperChecker
+      True
+      >>> wrapperChecker.check(wrapper, 'b')
+      >>> wrapperChecker.check(wrapper, 'a')  # doctest: +ELLIPSIS
+      Traceback (most recent call last):
+      ForbiddenAttribute: ('a', <zope.decorator.Foo object ...>)
+
+    (Note that the object description says `Foo` because the object is a
+    proxy and generally looks and acts like the object it's proxying.)
+
+    When we access wrapper's ``__Security_checker__`` attribute, we invoke
+    the decorated security checker descriptor. The decorator's job is to make
+    sure checkers from both objects are used when available. In this case,
+    because both objects have checkers, we get a combined checker:
+
+      >>> checker = wrapper.__Security_checker__
+      >>> type(checker)
+      <class 'zope.security.checker.CombinedChecker'>
+      >>> checker.check(wrapper, 'a')
+      >>> checker.check(wrapper, 'b')
+
+    The decorator checker will work even with security proxied objects. To
+    illustrate, we'll proxify `foo`:
+
+      >>> from zope.security.proxy import ProxyFactory
+      >>> secure_foo = ProxyFactory(foo)
+      >>> secure_foo.a
+      'a'
+      >>> secure_foo.b  # doctest: +ELLIPSIS
+      Traceback (most recent call last):
+      ForbiddenAttribute: ('b', <zope.decorator.Foo object ...>)
+
+    when we wrap the secured `foo`:
+
+      >>> wrapper = Wrapper(secure_foo)
+
+    we still get a combined checker:
+
+      >>> checker = wrapper.__Security_checker__
+      >>> type(checker)
+      <class 'zope.security.checker.CombinedChecker'>
+      >>> checker.check(wrapper, 'a')
+      >>> checker.check(wrapper, 'b')
+
+    The decorator checker has three other scenarios:
+
+      - the wrapper has a checker but the proxied object doesn't
+      - the proxied object has a checker but the wrapper doesn't
+      - neither the wrapper nor the proxied object have checkers
+
+    When the wrapper has a checker but the proxied object doesn't:
+
+      >>> from zope.security.checker import NoProxy, _checkers
+      >>> del _checkers[Foo]
+      >>> defineChecker(Foo, NoProxy)
+      >>> selectChecker(foo) is None
+      True
+      >>> selectChecker(wrapper) is wrapperChecker
+      True
+
+    the decorator uses only the wrapper checker:
+
+      >>> wrapper = Wrapper(foo)
+      >>> wrapper.__Security_checker__ is wrapperChecker
+      True
+
+    When the proxied object has a checker but the wrapper doesn't:
+
+      >>> del _checkers[Wrapper]
+      >>> defineChecker(Wrapper, NoProxy)
+      >>> selectChecker(wrapper) is None
+      True
+      >>> del _checkers[Foo]
+      >>> defineChecker(Foo, fooChecker)
+      >>> selectChecker(foo) is fooChecker
+      True
+
+    the decorator uses only the proxied object checker:
+
+      >>> wrapper.__Security_checker__ is fooChecker
+      True
+
+    Finally, if neither the wrapper not the proxied have checkers:
+
+      >>> del _checkers[Foo]
+      >>> defineChecker(Foo, NoProxy)
+      >>> selectChecker(foo) is None
+      True
+      >>> selectChecker(wrapper) is None
+      True
+
+    the decorator doesn't have a checker:
+
+      >>> wrapper.__Security_checker__ is None
+      True
+
+    """
+    def __get__(self, inst, cls=None):
+        if inst is None:
+            return self
+        else:
+            proxied_object = getProxiedObject(inst)
+            if type(proxied_object) is Proxy:
+                checker = getChecker(proxied_object)
+            else:
+                checker = getattr(proxied_object, '__Security_checker__', None)
+                if checker is None:
+                    checker = selectChecker(proxied_object)
+            wrapper_checker = selectChecker(inst)
+            if wrapper_checker is None:
+                return checker
+            elif checker is None:
+                return wrapper_checker
+            else:
+                return CombinedChecker(wrapper_checker, checker)
+
+    def __set__(self, inst, value):
+        raise TypeError("Can't set __Security_checker__ on a decorated object")
+
+
+class SecurityCheckerDecoratorBase(ProxyBase):
+    """Base class for a proxy that provides additional security declarations."""
+
+    __Security_checker__ = DecoratedSecurityCheckerDescriptor()
+
+
+class DecoratorBase(SpecificationDecoratorBase, SecurityCheckerDecoratorBase):
+    """Base class for a proxy that provides both additional interfaces and
+    security declarations."""
+
+



More information about the Zope3-Checkins mailing list