Skip to content

Commit 732337d

Browse files
committed
Fallback to fget.__name__ if name is not set
Raise AttributeError if no fget or it doesn't have `__name__`.
1 parent 5dfa4f4 commit 732337d

File tree

4 files changed

+95
-18
lines changed

4 files changed

+95
-18
lines changed

Lib/inspect.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -834,9 +834,8 @@ def _finddoc(obj):
834834
cls = self.__class__
835835
# Should be tested before isdatadescriptor().
836836
elif isinstance(obj, property):
837-
func = obj.fget
838-
name = obj.__name__ or func.__name__
839-
cls = _findclass(func)
837+
name = obj.__name__
838+
cls = _findclass(obj.fget)
840839
if cls is None or getattr(cls, name) is not obj:
841840
return None
842841
elif ismethoddescriptor(obj) or isdatadescriptor(obj):

Lib/pydoc.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,8 @@ def _finddoc(obj):
127127
cls = self.__class__
128128
# Should be tested before isdatadescriptor().
129129
elif isinstance(obj, property):
130-
func = obj.fget
131-
name = obj.__name__ or func.__name__
132-
cls = _findclass(func)
130+
name = obj.__name__
131+
cls = _findclass(obj.fget)
133132
if cls is None or getattr(cls, name) is not obj:
134133
return None
135134
elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):

Lib/test/test_property.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,21 +205,54 @@ def test_property_name(self):
205205
def getter(self):
206206
return 42
207207

208+
def setter(self, value):
209+
pass
210+
208211
class A:
209212
@property
210213
def foo(self):
211214
return 1
212215

216+
@foo.setter
217+
def oof(self, value):
218+
pass
219+
213220
bar = property(getter)
221+
baz = property(None, setter)
214222

215223
self.assertEqual(A.foo.__name__, 'foo')
224+
self.assertEqual(A.oof.__name__, 'oof')
216225
self.assertEqual(A.bar.__name__, 'bar')
226+
self.assertEqual(A.baz.__name__, 'baz')
217227

218-
A.baz = property(getter)
219-
self.assertIsNone(A.baz.__name__)
220-
A.baz.__name__ = 'mybaz'
221-
self.assertEqual(A.baz.__name__, 'mybaz')
228+
A.quux = property(getter)
229+
self.assertEqual(A.quux.__name__, 'getter')
230+
A.quux.__name__ = 'myquux'
231+
self.assertEqual(A.quux.__name__, 'myquux')
222232
self.assertEqual(A.bar.__name__, 'bar') # not affected
233+
A.quux.__name__ = None
234+
self.assertIsNone(A.quux.__name__)
235+
236+
with self.assertRaisesRegex(
237+
AttributeError, "'property' object has no attribute '__name__'"
238+
):
239+
property(None, setter).__name__
240+
241+
with self.assertRaisesRegex(
242+
AttributeError, "'property' object has no attribute '__name__'"
243+
):
244+
property(1).__name__
245+
246+
class Err:
247+
def __getattr__(self, attr):
248+
raise RuntimeError('fail')
249+
250+
p = property(Err())
251+
with self.assertRaisesRegex(RuntimeError, 'fail'):
252+
p.__name__
253+
254+
p.__name__ = 'not_fail'
255+
self.assertEqual(p.__name__, 'not_fail')
223256

224257
def test_property_set_name_incorrect_args(self):
225258
p = property()

Objects/descrobject.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,17 +1528,17 @@ class property(object):
15281528
if inst is None:
15291529
return self
15301530
if self.__get is None:
1531-
raise AttributeError, "property has no getter"
1531+
raise AttributeError("property has no getter")
15321532
return self.__get(inst)
15331533
15341534
def __set__(self, inst, value):
15351535
if self.__set is None:
1536-
raise AttributeError, "property has no setter"
1536+
raise AttributeError("property has no setter")
15371537
return self.__set(inst, value)
15381538
15391539
def __delete__(self, inst):
15401540
if self.__del is None:
1541-
raise AttributeError, "property has no deleter"
1541+
raise AttributeError("property has no deleter")
15421542
return self.__del(inst)
15431543
15441544
*/
@@ -1551,7 +1551,6 @@ static PyMemberDef property_members[] = {
15511551
{"fset", _Py_T_OBJECT, offsetof(propertyobject, prop_set), Py_READONLY},
15521552
{"fdel", _Py_T_OBJECT, offsetof(propertyobject, prop_del), Py_READONLY},
15531553
{"__doc__", _Py_T_OBJECT, offsetof(propertyobject, prop_doc), 0},
1554-
{"__name__", _Py_T_OBJECT, offsetof(propertyobject, prop_name), 0},
15551554
{0}
15561555
};
15571556

@@ -1633,6 +1632,20 @@ property_dealloc(PyObject *self)
16331632
Py_TYPE(self)->tp_free(self);
16341633
}
16351634

1635+
static int
1636+
property_name(propertyobject *prop, PyObject **name)
1637+
{
1638+
if (prop->prop_name != NULL) {
1639+
*name = Py_NewRef(prop->prop_name);
1640+
return 1;
1641+
}
1642+
if (prop->prop_get == NULL) {
1643+
*name = NULL;
1644+
return 0;
1645+
}
1646+
return PyObject_GetOptionalAttr(prop->prop_get, &_Py_ID(__name__), name);
1647+
}
1648+
16361649
static PyObject *
16371650
property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
16381651
{
@@ -1642,11 +1655,15 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
16421655

16431656
propertyobject *gs = (propertyobject *)self;
16441657
if (gs->prop_get == NULL) {
1658+
PyObject *propname;
1659+
if (property_name(gs, &propname) < 0) {
1660+
return NULL;
1661+
}
16451662
PyObject *qualname = PyType_GetQualName(Py_TYPE(obj));
1646-
if (gs->prop_name != NULL && qualname != NULL) {
1663+
if (propname != NULL && qualname != NULL) {
16471664
PyErr_Format(PyExc_AttributeError,
16481665
"property %R of %R object has no getter",
1649-
gs->prop_name,
1666+
propname,
16501667
qualname);
16511668
}
16521669
else if (qualname != NULL) {
@@ -1657,6 +1674,7 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
16571674
PyErr_SetString(PyExc_AttributeError,
16581675
"property has no getter");
16591676
}
1677+
Py_XDECREF(propname);
16601678
Py_XDECREF(qualname);
16611679
return NULL;
16621680
}
@@ -1678,16 +1696,20 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
16781696
}
16791697

16801698
if (func == NULL) {
1699+
PyObject *propname;
1700+
if (property_name(gs, &propname) < 0) {
1701+
return -1;
1702+
}
16811703
PyObject *qualname = NULL;
16821704
if (obj != NULL) {
16831705
qualname = PyType_GetQualName(Py_TYPE(obj));
16841706
}
1685-
if (gs->prop_name != NULL && qualname != NULL) {
1707+
if (propname != NULL && qualname != NULL) {
16861708
PyErr_Format(PyExc_AttributeError,
16871709
value == NULL ?
16881710
"property %R of %R object has no deleter" :
16891711
"property %R of %R object has no setter",
1690-
gs->prop_name,
1712+
propname,
16911713
qualname);
16921714
}
16931715
else if (qualname != NULL) {
@@ -1703,6 +1725,7 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
17031725
"property has no deleter" :
17041726
"property has no setter");
17051727
}
1728+
Py_XDECREF(propname);
17061729
Py_XDECREF(qualname);
17071730
return -1;
17081731
}
@@ -1888,6 +1911,28 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
18881911
return 0;
18891912
}
18901913

1914+
static PyObject *
1915+
property_get__name__(propertyobject *prop, void *Py_UNUSED(ignored))
1916+
{
1917+
PyObject *name;
1918+
if (property_name(prop, &name) < 0) {
1919+
return NULL;
1920+
}
1921+
if (name == NULL) {
1922+
PyErr_SetString(PyExc_AttributeError,
1923+
"'property' object has no attribute '__name__'");
1924+
}
1925+
return name;
1926+
}
1927+
1928+
static int
1929+
property_set__name__(propertyobject *prop, PyObject *value,
1930+
void *Py_UNUSED(ignored))
1931+
{
1932+
Py_XSETREF(prop->prop_name, Py_XNewRef(value));
1933+
return 0;
1934+
}
1935+
18911936
static PyObject *
18921937
property_get___isabstractmethod__(propertyobject *prop, void *closure)
18931938
{
@@ -1918,6 +1963,7 @@ property_get___isabstractmethod__(propertyobject *prop, void *closure)
19181963
}
19191964

19201965
static PyGetSetDef property_getsetlist[] = {
1966+
{"__name__", (getter)property_get__name__, (setter)property_set__name__},
19211967
{"__isabstractmethod__",
19221968
(getter)property_get___isabstractmethod__, NULL,
19231969
NULL,

0 commit comments

Comments
 (0)