[Zope3-checkins] CVS: Zope3/src/zope/context/tests - test_wrapperinteraction.py:1.7

Steve Alexander steve@cat-box.net
Sat, 7 Jun 2003 15:05:21 -0400


Update of /cvs-repository/Zope3/src/zope/context/tests
In directory cvs.zope.org:/tmp/cvs-serv12837/src/zope/context/tests

Modified Files:
	test_wrapperinteraction.py 
Log Message:
Added a ContextAware class.
This takes a different approach than the ContextAware marker class that
had been used up until a few days ago.
The problem with the marker class is that its effects were apparent even
if your class derives from ContextAware via some hierarchy of base classes.

The approach in this checkin uses a metaclass that proxies descriptors
defined in a class with appropriate ContextDescriptors. ContextAware
must be present explicitly and visibly in a class' base-classes to
trigger this context-aware behaviour.

If this appears to be too odd a behaviour for a base class, then
another approach is to make ContextAware not a base class, but instead
a form of "class advice", like implements() from zope.interface.
So, something like this:
  class Foo:
      thingsDefinedInThisClassShallBeContextAware()



=== Zope3/src/zope/context/tests/test_wrapperinteraction.py 1.6 => 1.7 ===
--- Zope3/src/zope/context/tests/test_wrapperinteraction.py:1.6	Sun Jun  1 11:59:40 2003
+++ Zope3/src/zope/context/tests/test_wrapperinteraction.py	Sat Jun  7 15:05:20 2003
@@ -471,12 +471,135 @@
         self.assertEqual(wrapped[0], 23)
         self.assertEqual(wrapped.__getitem__(0), 23)
 
+
+class LoggingDescriptor:
+    def __init__(self, getval=None, doc=''):
+        self.getval = getval
+        self.doc = doc
+        self.log = []
+
+    def __get__(self, inst, cls=None):
+        self.log.append(('get', inst, cls))
+        return self.getval
+
+    def getDoc(self):
+        return self.doc
+    __doc__ = property(getDoc)
+
+    def checkClear(self):
+        l = self.log
+        self.log = []
+        return l
+
+class LoggingDataDescriptor(LoggingDescriptor):
+
+    def __set__(self, inst, value):
+        self.log.append(('set', inst, value))
+
+    def __delete__(self, inst):
+        self.log.append(('del', inst))
+
+class TestContextAwareDescriptor(unittest.TestCase):
+
+    def newInstance(self, value):
+        from zope.context import ContextAwareDescriptor
+        return ContextAwareDescriptor(value)
+
+    def setUp(self):
+        self.d = LoggingDataDescriptor(23, 'my doc')
+        self.cd = self.newInstance(self.d)
+
+    def test_is_ContextDescriptor(self):
+        from zope.context import ContextDescriptor
+        self.assert_(isinstance(self.cd, ContextDescriptor))
+
+    def test_descriptor(self):
+        inst = object()  # any old object
+        cls = object     # any old type
+        cd = self.cd
+        d = self.d
+
+        self.assertEquals(cd.__get__(inst, cls), 23)
+        self.assertEquals(d.checkClear(), [('get', inst, cls)])
+        self.assertEquals(cd.__doc__, d.__doc__)
+
+
+class TestContextAwareDataDescriptor(TestContextAwareDescriptor):
+
+    def newInstance(self, value):
+        from zope.context import ContextAwareDataDescriptor
+        return ContextAwareDataDescriptor(value)
+
+    def test_datadescriptor(self):
+        inst = object()  # any old object
+        cls = object     # any old type
+        cd = self.cd
+        d = self.d
+
+        cd.__set__(inst, 88)
+        self.assertEquals(d.checkClear(), [('set', inst, 88)])
+        cd.__delete__(inst)
+        self.assertEquals(d.checkClear(), [('del', inst)])
+
+
+class TestContextAware(unittest.TestCase):
+
+    def test_ContextAwareMetaClass(self):
+        from zope.context import ContextAwareMetaClass, ContextAware
+        from zope.context import ContextMethod, ContextProperty
+        normal_data_descriptor = LoggingDataDescriptor(23)
+        normal_descriptor = LoggingDescriptor(23)
+        context_method = ContextMethod(lambda s: None)
+        context_descriptor = ContextProperty()
+
+        # Check that nothing in particular happens if the class doesn't
+        # derive directly from ContextAware.
+        class X:
+            __metaclass__ = ContextAwareMetaClass
+
+            ndd = normal_data_descriptor
+            nd = normal_descriptor
+
+        self.assert_(X.ndd is normal_data_descriptor)
+        self.assert_(X.nd is normal_descriptor)
+
+        class X(ContextAware):
+
+            cm = context_method
+            ndd = normal_data_descriptor
+            nd = normal_descriptor
+            cd = context_descriptor
+
+        self.assert_(X.cm is context_method)
+        self.assert_(X.cd is context_descriptor)
+        normal_descriptor.checkClear()
+        self.assertEquals(X.nd, 23)
+        self.assertEquals(normal_descriptor.checkClear(),
+                          [('get', None, X)]
+                          )
+        normal_data_descriptor.checkClear()
+        self.assertEquals(X.ndd, 23)
+        self.assertEquals(normal_data_descriptor.checkClear(),
+                          [('get', None, X)]
+                          )
+        X.ndd = 42
+        # We've established that our descriptor is behind the context
+        # descriptor. The rest is covered by the ContextAware(Data)Descriptor
+        # unit tests.
+
+    def test_ContextAware(self):
+        from zope.context import ContextAware, ContextAwareMetaClass
+        self.assert_(type(ContextAware) is ContextAwareMetaClass)
+
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(TestNewStyleClass),
         unittest.makeSuite(TestNewStyleClassWithSlots),
         unittest.makeSuite(TestClassicClass),
         unittest.makeSuite(TestWrapperOnObjectsWithDifferentSlots),
+        unittest.makeSuite(TestContextAwareDataDescriptor),
+        unittest.makeSuite(TestContextAwareDescriptor),
+        unittest.makeSuite(TestContextAware),
         ))