[Zope3-checkins] SVN: Zope3/branches/jim-adapter/src/zope/proxy/ Fix a bug that caused non-data descriptors to hide proxied-object data.

Jim Fulton jim at zope.com
Sun Apr 2 12:59:05 EDT 2006


Log message for revision 66294:
  Fix a bug that caused non-data descriptors to hide proxied-object data.
  

Changed:
  U   Zope3/branches/jim-adapter/src/zope/proxy/__init__.py
  U   Zope3/branches/jim-adapter/src/zope/proxy/_zope_proxy_proxy.c
  U   Zope3/branches/jim-adapter/src/zope/proxy/tests/test_proxy.py

-=-
Modified: Zope3/branches/jim-adapter/src/zope/proxy/__init__.py
===================================================================
--- Zope3/branches/jim-adapter/src/zope/proxy/__init__.py	2006-04-02 10:47:12 UTC (rev 66293)
+++ Zope3/branches/jim-adapter/src/zope/proxy/__init__.py	2006-04-02 16:59:05 UTC (rev 66294)
@@ -29,3 +29,6 @@
     while isProxy(p):
         p = getProxiedObject(p)
         yield p
+
+def non_overridable(func):
+    return property(lambda self: func.__get__(self))

Modified: Zope3/branches/jim-adapter/src/zope/proxy/_zope_proxy_proxy.c
===================================================================
--- Zope3/branches/jim-adapter/src/zope/proxy/_zope_proxy_proxy.c	2006-04-02 10:47:12 UTC (rev 66293)
+++ Zope3/branches/jim-adapter/src/zope/proxy/_zope_proxy_proxy.c	2006-04-02 16:59:05 UTC (rev 66294)
@@ -234,6 +234,18 @@
         if (descriptor != NULL) {
             if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS)
                 && descriptor->ob_type->tp_descr_get != NULL) {
+
+              if (descriptor->ob_type->tp_descr_set == NULL)
+                {
+                  res = PyObject_GetAttr(wrapped, name);
+                  if (res != NULL)
+                    goto finally;
+                  if (PyErr_ExceptionMatches(PyExc_AttributeError))
+                    PyErr_Clear();
+                  else
+                    goto finally;
+                }
+
                 res = descriptor->ob_type->tp_descr_get(
                         descriptor,
                         self,
@@ -278,17 +290,13 @@
         Py_INCREF(name);
 
     descriptor = WrapperType_Lookup(self->ob_type, name);
-    if (descriptor != NULL) {
-        if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) &&
-            descriptor->ob_type->tp_descr_set != NULL) {
-            res = descriptor->ob_type->tp_descr_set(descriptor, self, value);
-        } else {
-            PyErr_Format(PyExc_TypeError,
-                "Tried to set attribute '%s' on wrapper, but it is not"
-                " a data descriptor", PyString_AS_STRING(name));
-        }
+    if (descriptor != NULL
+        && PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) 
+        && descriptor->ob_type->tp_descr_set != NULL) 
+      {
+        res = descriptor->ob_type->tp_descr_set(descriptor, self, value);
         goto finally;
-    }
+      }
 
     wrapped = Proxy_GET_OBJECT(self);
     if (wrapped == NULL) {

Modified: Zope3/branches/jim-adapter/src/zope/proxy/tests/test_proxy.py
===================================================================
--- Zope3/branches/jim-adapter/src/zope/proxy/tests/test_proxy.py	2006-04-02 10:47:12 UTC (rev 66293)
+++ Zope3/branches/jim-adapter/src/zope/proxy/tests/test_proxy.py	2006-04-02 16:59:05 UTC (rev 66294)
@@ -21,6 +21,7 @@
 
 from zope.testing.doctestunit import DocTestSuite
 from zope.proxy import ProxyBase
+import zope.proxy
 
 class Thing:
     """This class is expected to be a classic class."""
@@ -552,7 +553,67 @@
     
     """
 
+def test_get_descriptors_in_proxy_class():
+    """
+    A non-data descriptor in a proxy class doesn't hide an attribute on
+    a proxied object or prevent writing the attribute.
 
+    >>> class ReadDescr(object):
+    ...     def __get__(self, i, c):
+    ...         return 'read'
+
+    >>> class MyProxy(ProxyBase):
+    ...    __slots__ = ()
+    ...
+    ...    z = ReadDescr()
+    ...    q = ReadDescr()
+
+    >>> class MyOb:
+    ...    q = 1
+
+    >>> o = MyOb()
+    >>> p = MyProxy(o)
+    >>> p.q
+    1
+
+    >>> p.z
+    'read'
+
+    >>> p.z = 1
+    >>> o.z, p.z
+    (1, 1)
+    
+    """
+
+def test_non_overridable():
+    """
+    Normally, methods defined in proxies are overridden by
+    methods of proxied objects.  This applies to all non-data
+    descriptors.  The non_overridable function can be used to
+    convert a non-data descriptor to a data descriptor that disallows
+    writes.  This function can be used as a decorator to make functions
+    defined in proxy classes take precedence over functions defined
+    in proxied objects.
+
+    
+    >>> class MyProxy(ProxyBase):
+    ...    __slots__ = ()
+    ...
+    ...    @zope.proxy.non_overridable
+    ...    def foo(self):
+    ...        return 'MyProxy foo'
+
+    >>> class MyOb:
+    ...    def foo(self):
+    ...        return 'MyOb foo'
+
+    >>> o = MyOb()
+    >>> p = MyProxy(o)
+    >>> p.foo()
+    'MyProxy foo'
+    
+    """
+
 def test_suite():
     suite = unittest.makeSuite(ProxyTestCase)
     suite.addTest(DocTestSuite())



More information about the Zope3-Checkins mailing list