bpo-27794: Add `name` attribute to `property` class (GH-23967)

diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index a8ce13c..16c695a 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -1490,6 +1490,7 @@ typedef struct {
     PyObject *prop_set;
     PyObject *prop_del;
     PyObject *prop_doc;
+    PyObject *prop_name;
     int getter_doc;
 } propertyobject;
 
@@ -1535,10 +1536,33 @@ property_deleter(PyObject *self, PyObject *deleter)
 }
 
 
+PyDoc_STRVAR(set_name_doc,
+             "Method to set name of a property.");
+
+static PyObject *
+property_set_name(PyObject *self, PyObject *args) {
+    if (PyTuple_GET_SIZE(args) != 2) {
+        PyErr_Format(
+                PyExc_TypeError,
+                "__set_name__() takes 2 positional arguments but %d were given",
+                PyTuple_GET_SIZE(args));
+        return NULL;
+    }
+
+    propertyobject *prop = (propertyobject *)self;
+    PyObject *name = PyTuple_GET_ITEM(args, 1);
+
+    Py_XINCREF(name);
+    Py_XSETREF(prop->prop_name, name);
+
+    Py_RETURN_NONE;
+}
+
 static PyMethodDef property_methods[] = {
     {"getter", property_getter, METH_O, getter_doc},
     {"setter", property_setter, METH_O, setter_doc},
     {"deleter", property_deleter, METH_O, deleter_doc},
+    {"__set_name__", property_set_name, METH_VARARGS, set_name_doc},
     {0}
 };
 
@@ -1553,6 +1577,7 @@ property_dealloc(PyObject *self)
     Py_XDECREF(gs->prop_set);
     Py_XDECREF(gs->prop_del);
     Py_XDECREF(gs->prop_doc);
+    Py_XDECREF(gs->prop_name);
     Py_TYPE(self)->tp_free(self);
 }
 
@@ -1566,7 +1591,12 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
 
     propertyobject *gs = (propertyobject *)self;
     if (gs->prop_get == NULL) {
-        PyErr_SetString(PyExc_AttributeError, "unreadable attribute");
+        if (gs->prop_name != NULL) {
+            PyErr_Format(PyExc_AttributeError, "unreadable attribute %R", gs->prop_name);
+        } else {
+            PyErr_SetString(PyExc_AttributeError, "unreadable attribute");
+        }
+
         return NULL;
     }
 
@@ -1584,10 +1614,18 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
     else
         func = gs->prop_set;
     if (func == NULL) {
-        PyErr_SetString(PyExc_AttributeError,
+        if (gs->prop_name != NULL) {
+            PyErr_Format(PyExc_AttributeError,
                         value == NULL ?
-                        "can't delete attribute" :
-                "can't set attribute");
+                        "can't delete attribute %R" :
+                        "can't set attribute %R",
+                        gs->prop_name);
+        } else {
+            PyErr_SetString(PyExc_AttributeError,
+                            value == NULL ?
+                            "can't delete attribute" :
+                            "can't set attribute");
+        }
         return -1;
     }
     if (value == NULL)
@@ -1634,6 +1672,9 @@ property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del)
     Py_DECREF(type);
     if (new == NULL)
         return NULL;
+
+    Py_XINCREF(pold->prop_name);
+    Py_XSETREF(((propertyobject *) new)->prop_name, pold->prop_name);
     return new;
 }
 
@@ -1695,6 +1736,8 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
     Py_XSETREF(self->prop_set, fset);
     Py_XSETREF(self->prop_del, fdel);
     Py_XSETREF(self->prop_doc, doc);
+    Py_XSETREF(self->prop_name, NULL);
+
     self->getter_doc = 0;
 
     /* if no docstring given and the getter has one, use that one */
@@ -1769,6 +1812,7 @@ property_traverse(PyObject *self, visitproc visit, void *arg)
     Py_VISIT(pp->prop_set);
     Py_VISIT(pp->prop_del);
     Py_VISIT(pp->prop_doc);
+    Py_VISIT(pp->prop_name);
     return 0;
 }