[Zope3-checkins] CVS: Zope3/src/zope/context - __init__.py:1.21

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


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

Modified Files:
	__init__.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/__init__.py 1.20 => 1.21 ===
--- Zope3/src/zope/context/__init__.py:1.20	Sat Jun  7 09:00:01 2003
+++ Zope3/src/zope/context/__init__.py	Sat Jun  7 15:05:20 2003
@@ -98,3 +98,53 @@
         inst = self.__inst
         return getattr(super(self.__class, getbaseobject(inst)), name
                        ).__get__(inst)
+
+class ContextAwareDescriptor(ContextDescriptor):
+    # TODO For speed, reimplement this in C
+
+    def __init__(self, descriptor):
+        self.descriptor = descriptor
+
+    def __get__(self, inst, cls=None):
+        return self.descriptor.__get__(inst, cls)
+
+    def getdoc(self):
+        return self.descriptor.__doc__
+
+    __doc__ = property(getdoc)
+
+class ContextAwareDataDescriptor(ContextAwareDescriptor):
+
+    def __set__(self, inst, value):
+        self.descriptor.__set__(inst, value)
+
+    def __delete__(self, inst):
+        self.descriptor.__delete__(inst)
+
+
+class ContextAwareMetaClass(type):
+
+    def __init__(self, name, bases, namespace):
+        # stub
+        super(ContextAwareMetaClass, self).__init__(name, bases, namespace)
+
+
+class ContextAware:
+    __metaclass__ = ContextAwareMetaClass
+
+
+def init_method(self, name, bases, namespace):
+    if ContextAware in bases:
+        for name, obj in namespace.items():
+            if not isinstance(obj, ContextDescriptor):
+                if getattr(obj, '__set__', None) is not None:
+                    d = ContextAwareDataDescriptor(obj)
+                    setattr(self, name, d)
+                    namespace[name] = d
+                elif getattr(obj, '__get__', None) is not None:
+                    m = ContextAwareDescriptor(obj)
+                    setattr(self, name, m)
+                    namespace[name] = m
+    super(ContextAwareMetaClass, self).__init__(name, bases, namespace)
+ContextAwareMetaClass.__init__ = init_method
+del init_method