Skip to content

Commit 05db108

Browse files
committed
gh-101860: Expose __name__ on property
Useful for introspection and consistent with functions and other descriptors.
1 parent 6ef6915 commit 05db108

File tree

7 files changed

+39
-14
lines changed

7 files changed

+39
-14
lines changed

Doc/howto/descriptor.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,41 +1000,41 @@ here is a pure Python equivalent:
10001000
if doc is None and fget is not None:
10011001
doc = fget.__doc__
10021002
self.__doc__ = doc
1003-
self._name = ''
1003+
self.__name__ = None
10041004

10051005
def __set_name__(self, owner, name):
1006-
self._name = name
1006+
self.__name__ = name
10071007

10081008
def __get__(self, obj, objtype=None):
10091009
if obj is None:
10101010
return self
10111011
if self.fget is None:
1012-
raise AttributeError(f"property '{self._name}' has no getter")
1012+
raise AttributeError(f"property '{self.__name__}' has no getter")
10131013
return self.fget(obj)
10141014

10151015
def __set__(self, obj, value):
10161016
if self.fset is None:
1017-
raise AttributeError(f"property '{self._name}' has no setter")
1017+
raise AttributeError(f"property '{self.__name__}' has no setter")
10181018
self.fset(obj, value)
10191019

10201020
def __delete__(self, obj):
10211021
if self.fdel is None:
1022-
raise AttributeError(f"property '{self._name}' has no deleter")
1022+
raise AttributeError(f"property '{self.__name__}' has no deleter")
10231023
self.fdel(obj)
10241024

10251025
def getter(self, fget):
10261026
prop = type(self)(fget, self.fset, self.fdel, self.__doc__)
1027-
prop._name = self._name
1027+
prop.__name__ = self.__name__
10281028
return prop
10291029

10301030
def setter(self, fset):
10311031
prop = type(self)(self.fget, fset, self.fdel, self.__doc__)
1032-
prop._name = self._name
1032+
prop.__name__ = self.__name__
10331033
return prop
10341034

10351035
def deleter(self, fdel):
10361036
prop = type(self)(self.fget, self.fset, fdel, self.__doc__)
1037-
prop._name = self._name
1037+
prop.__name__ = self.__name__
10381038
return prop
10391039

10401040
.. testcode::

Lib/inspect.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -825,9 +825,8 @@ def _finddoc(obj):
825825
cls = self.__class__
826826
# Should be tested before isdatadescriptor().
827827
elif isinstance(obj, property):
828-
func = obj.fget
829-
name = func.__name__
830-
cls = _findclass(func)
828+
name = obj.__name__
829+
cls = _findclass(obj.fget)
831830
if cls is None or getattr(cls, name) is not obj:
832831
return None
833832
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 = 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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,26 @@ def __doc__(cls):
204204
return 'Second'
205205
self.assertEqual(A.__doc__, 'Second')
206206

207+
def test_property_name(self):
208+
def getter(self):
209+
return 42
210+
211+
class A:
212+
@property
213+
def foo(self):
214+
return 1
215+
216+
bar = property(getter)
217+
218+
self.assertEqual(A.foo.__name__, 'foo')
219+
self.assertEqual(A.bar.__name__, 'bar')
220+
221+
A.baz = property(getter)
222+
self.assertIsNone(A.baz.__name__)
223+
A.baz.__name__ = 'mybaz'
224+
self.assertEqual(A.baz.__name__, 'mybaz')
225+
self.assertEqual(A.bar.__name__, 'bar') # not affected
226+
207227
def test_property_set_name_incorrect_args(self):
208228
p = property()
209229

Lib/test/test_pydoc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,7 @@ def area(self):
12651265
return self.w * self.h
12661266

12671267
self.assertEqual(self._get_summary_lines(Rect.area), """\
1268+
area
12681269
Area of the rect
12691270
""")
12701271
self.assertIn("""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Expose ``__name__`` attribute on property.

Objects/descrobject.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,6 +1480,10 @@ class property(object):
14801480
self.__set = fset
14811481
self.__del = fdel
14821482
self.__doc__ = doc
1483+
self.__name__ = None
1484+
1485+
def __set_name__(self, owner, name):
1486+
self.__name__ = name
14831487
14841488
def __get__(self, inst, type=None):
14851489
if inst is None:
@@ -1508,6 +1512,7 @@ static PyMemberDef property_members[] = {
15081512
{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
15091513
{"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY},
15101514
{"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), 0},
1515+
{"__name__", T_OBJECT, offsetof(propertyobject, prop_name), 0},
15111516
{0}
15121517
};
15131518

0 commit comments

Comments
 (0)