Skip to content

Commit e5ce6b7

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

File tree

4 files changed

+34
-8
lines changed

4 files changed

+34
-8
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/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

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)