[Python-checkins] r72304 - in python/branches/release30-maint: Lib/test/test_property.py Misc/NEWS Objects/descrobject.c

r.david.murray python-checkins at python.org
Tue May 5 03:01:52 CEST 2009


Author: r.david.murray
Date: Tue May  5 03:01:52 2009
New Revision: 72304

Log:
Merged revisions 72301 via svnmerge from 
svn+ssh://pythondev@svn.python.org/python/branches/py3k

................
  r72301 | r.david.murray | 2009-05-04 18:59:07 -0400 (Mon, 04 May 2009) | 13 lines
  
  Merged revisions 72299 via svnmerge from 
  svn+ssh://pythondev@svn.python.org/python/trunk
  
  ........
    r72299 | r.david.murray | 2009-05-04 18:16:24 -0400 (Mon, 04 May 2009) | 7 lines
    
    Fix issue 5890: (property subclass shadows __doc__ string) by inserting
    the __doc__ into the subclass instance __dict__.  The fix refactors
    property_copy to call property_init in such a way that the __doc__
    logic is re-executed correctly when getter_doc is 1, thus simplifying
    property_copy.
  ........
................


Modified:
   python/branches/release30-maint/   (props changed)
   python/branches/release30-maint/Lib/test/test_property.py
   python/branches/release30-maint/Misc/NEWS
   python/branches/release30-maint/Objects/descrobject.c

Modified: python/branches/release30-maint/Lib/test/test_property.py
==============================================================================
--- python/branches/release30-maint/Lib/test/test_property.py	(original)
+++ python/branches/release30-maint/Lib/test/test_property.py	Tue May  5 03:01:52 2009
@@ -60,6 +60,22 @@
         """The decorator does not use this doc string"""
         return self._spam
 
+class PropertySubNewGetter(BaseClass):
+    @BaseClass.spam.getter
+    def spam(self):
+        """new docstring"""
+        return 5
+
+class PropertyNewGetter(object):
+    @property
+    def spam(self):
+        """original docstring"""
+        return 1
+    @spam.getter
+    def spam(self):
+        """new docstring"""
+        return 8
+
 class PropertyTests(unittest.TestCase):
     def test_property_decorator_baseclass(self):
         # see #1620
@@ -91,8 +107,106 @@
         self.assertEqual(base.__class__.spam.__doc__, "spam spam spam")
         self.assertEqual(sub.__class__.spam.__doc__, "spam spam spam")
 
+    def test_property_getter_doc_override(self):
+        newgettersub = PropertySubNewGetter()
+        self.assertEqual(newgettersub.spam, 5)
+        self.assertEqual(newgettersub.__class__.spam.__doc__, "new docstring")
+        newgetter = PropertyNewGetter()
+        self.assertEqual(newgetter.spam, 8)
+        self.assertEqual(newgetter.__class__.spam.__doc__, "new docstring")
+
+
+# Issue 5890: subclasses of property do not preserve method __doc__ strings
+class PropertySub(property):
+    """This is a subclass of property"""
+
+class PropertySubSlots(property):
+    """This is a subclass of property that defines __slots__"""
+    __slots__ = ()
+
+class PropertySubclassTests(unittest.TestCase):
+
+    def test_docstring_copy(self):
+        class Foo(object):
+            @PropertySub
+            def spam(self):
+                """spam wrapped in property subclass"""
+                return 1
+        self.assertEqual(
+            Foo.spam.__doc__,
+            "spam wrapped in property subclass")
+
+    def test_slots_docstring_copy_exception(self):
+        try:
+            class Foo(object):
+                @PropertySubSlots
+                def spam(self):
+                    """Trying to copy this docstring will raise an exception"""
+                    return 1
+        except AttributeError:
+            pass
+        else:
+            raise Exception("AttributeError not raised")
+
+    def test_property_setter_copies_getter_docstring(self):
+        class Foo(object):
+            def __init__(self): self._spam = 1
+            @PropertySub
+            def spam(self):
+                """spam wrapped in property subclass"""
+                return self._spam
+            @spam.setter
+            def spam(self, value):
+                """this docstring is ignored"""
+                self._spam = value
+        foo = Foo()
+        self.assertEqual(foo.spam, 1)
+        foo.spam = 2
+        self.assertEqual(foo.spam, 2)
+        self.assertEqual(
+            Foo.spam.__doc__,
+            "spam wrapped in property subclass")
+        class FooSub(Foo):
+            @Foo.spam.setter
+            def spam(self, value):
+                """another ignored docstring"""
+                self._spam = 'eggs'
+        foosub = FooSub()
+        self.assertEqual(foosub.spam, 1)
+        foosub.spam = 7
+        self.assertEqual(foosub.spam, 'eggs')
+        self.assertEqual(
+            FooSub.spam.__doc__,
+            "spam wrapped in property subclass")
+
+    def test_property_new_getter_new_docstring(self):
+
+        class Foo(object):
+            @PropertySub
+            def spam(self):
+                """a docstring"""
+                return 1
+            @spam.getter
+            def spam(self):
+                """a new docstring"""
+                return 2
+        self.assertEqual(Foo.spam.__doc__, "a new docstring")
+        class FooBase(object):
+            @PropertySub
+            def spam(self):
+                """a docstring"""
+                return 1
+        class Foo2(FooBase):
+            @FooBase.spam.getter
+            def spam(self):
+                """a new docstring"""
+                return 2
+        self.assertEqual(Foo.spam.__doc__, "a new docstring")
+
+
+
 def test_main():
-    run_unittest(PropertyTests)
+    run_unittest(PropertyTests, PropertySubclassTests)
 
 if __name__ == '__main__':
     test_main()

Modified: python/branches/release30-maint/Misc/NEWS
==============================================================================
--- python/branches/release30-maint/Misc/NEWS	(original)
+++ python/branches/release30-maint/Misc/NEWS	Tue May  5 03:01:52 2009
@@ -12,6 +12,10 @@
 Core and Builtins
 -----------------
 
+- Issue #5890: in subclasses of 'property' the __doc__ attribute was
+  shadowed by classtype's, even if it was None.  property now
+  inserts the __doc__ into the subclass instance __dict__.
+
 - Issue #5724: (See also issue #4575.) Fix Py_IS_INFINITY macro to
   work correctly on x87 FPUs: it now forces its argument to double
   before testing for infinity.

Modified: python/branches/release30-maint/Objects/descrobject.c
==============================================================================
--- python/branches/release30-maint/Objects/descrobject.c	(original)
+++ python/branches/release30-maint/Objects/descrobject.c	Tue May  5 03:01:52 2009
@@ -1246,25 +1246,19 @@
 	}
 	if (doc == NULL || doc == Py_None) {
 		Py_XDECREF(doc);
-		doc = pold->prop_doc ? pold->prop_doc : Py_None;
+		if (pold->getter_doc && get != Py_None) {
+			/* make _init use __doc__ from getter */
+			doc = Py_None;
+		}
+		else {
+			doc = pold->prop_doc ? pold->prop_doc : Py_None;
+		}
 	}
-	
+
 	new =  PyObject_CallFunction(type, "OOOO", get, set, del, doc);
 	Py_DECREF(type);
 	if (new == NULL)
 		return NULL;
-	pnew = (propertyobject *)new;
-	
-	if (pold->getter_doc && get != Py_None) {
-		PyObject *get_doc = PyObject_GetAttrString(get, "__doc__");
-		if (get_doc != NULL) {
-			Py_XDECREF(pnew->prop_doc);
-			pnew->prop_doc = get_doc;  /* get_doc already INCREF'd by GetAttr */
-			pnew->getter_doc = 1;
-		} else {
-			PyErr_Clear();
-		}
-	}
 	return new;
 }
 
@@ -1301,8 +1295,21 @@
 	if ((doc == NULL || doc == Py_None) && get != NULL) {
 		PyObject *get_doc = PyObject_GetAttrString(get, "__doc__");
 		if (get_doc != NULL) {
-			Py_XDECREF(prop->prop_doc);
-			prop->prop_doc = get_doc;  /* get_doc already INCREF'd by GetAttr */
+			/* get_doc already INCREF'd by GetAttr */
+			if (Py_TYPE(self)==&PyProperty_Type) {
+				Py_XDECREF(prop->prop_doc);
+				prop->prop_doc = get_doc;
+			} else {
+				/* Put __doc__ in dict of the subclass instance instead,
+				otherwise it gets shadowed by class's __doc__. */
+				if (PyObject_SetAttrString(self, "__doc__", get_doc) != 0)
+				{
+					/* DECREF for props handled by _dealloc */
+					Py_DECREF(get_doc);
+					return -1;
+	                        }
+                                Py_DECREF(get_doc);
+			}
 			prop->getter_doc = 1;
 		} else {
 			PyErr_Clear();


More information about the Python-checkins mailing list