diff options
author | Gregory P. Smith <greg@krypto.org> | 2023-06-05 03:18:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-05 03:18:15 (GMT) |
commit | 418befd75d4d0d1cba83d8b81e1a7bcc9a65be8e (patch) | |
tree | 3797b22239be45fb058d56c9895345c40dfe5cc0 /Objects | |
parent | 1237fb6a4b177ce8f750949b9006c58f9f22942e (diff) | |
download | cpython-418befd75d4d0d1cba83d8b81e1a7bcc9a65be8e.zip cpython-418befd75d4d0d1cba83d8b81e1a7bcc9a65be8e.tar.gz cpython-418befd75d4d0d1cba83d8b81e1a7bcc9a65be8e.tar.bz2 |
gh-98963: Restore the ability to have a dict-less property. (#105262)
Ignore doc string assignment failures in `property` as has been the
behavior of all past Python releases.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/descrobject.c | 45 |
1 files changed, 37 insertions, 8 deletions
diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 17c0c85..72ac470 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1485,7 +1485,10 @@ class property(object): self.__get = fget self.__set = fset self.__del = fdel - self.__doc__ = doc + try: + self.__doc__ = doc + except AttributeError: # read-only or dict-less class + pass def __get__(self, inst, type=None): if inst is None: @@ -1791,6 +1794,19 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset, if (rc <= 0) { return rc; } + if (!Py_IS_TYPE(self, &PyProperty_Type) && + prop_doc != NULL && prop_doc != Py_None) { + // This oddity preserves the long existing behavior of surfacing + // an AttributeError when using a dict-less (__slots__) property + // subclass as a decorator on a getter method with a docstring. + // See PropertySubclassTest.test_slots_docstring_copy_exception. + int err = PyObject_SetAttr( + (PyObject *)self, &_Py_ID(__doc__), prop_doc); + if (err < 0) { + Py_DECREF(prop_doc); // release our new reference. + return -1; + } + } if (prop_doc == Py_None) { prop_doc = NULL; Py_DECREF(Py_None); @@ -1806,19 +1822,32 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset, if (Py_IS_TYPE(self, &PyProperty_Type)) { Py_XSETREF(self->prop_doc, prop_doc); } else { - /* If this is a property subclass, put __doc__ - in dict of the subclass instance instead, - otherwise it gets shadowed by __doc__ in the - class's dict. */ + /* If this is a property subclass, put __doc__ in the dict + or designated slot of the subclass instance instead, otherwise + it gets shadowed by __doc__ in the class's dict. */ if (prop_doc == NULL) { prop_doc = Py_NewRef(Py_None); } int err = PyObject_SetAttr( (PyObject *)self, &_Py_ID(__doc__), prop_doc); - Py_XDECREF(prop_doc); - if (err < 0) - return -1; + Py_DECREF(prop_doc); + if (err < 0) { + assert(PyErr_Occurred()); + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + // https://github.com/python/cpython/issues/98963#issuecomment-1574413319 + // Python silently dropped this doc assignment through 3.11. + // We preserve that behavior for backwards compatibility. + // + // If we ever want to deprecate this behavior, only raise a + // warning or error when proc_doc is not None so that + // property without a specific doc= still works. + return 0; + } else { + return -1; + } + } } return 0; |