Skip to content

Commit

Permalink
pythongh-101860: Expose __name__ on property
Browse files Browse the repository at this point in the history
Useful for introspection and consistent with functions and other
descriptors.
  • Loading branch information
eltoder committed Feb 13, 2023
1 parent 6ef6915 commit 05db108
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 14 deletions.
16 changes: 8 additions & 8 deletions Doc/howto/descriptor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1000,41 +1000,41 @@ here is a pure Python equivalent:
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
self._name = ''
self.__name__ = None

def __set_name__(self, owner, name):
self._name = name
self.__name__ = name

def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError(f"property '{self._name}' has no getter")
raise AttributeError(f"property '{self.__name__}' has no getter")
return self.fget(obj)

def __set__(self, obj, value):
if self.fset is None:
raise AttributeError(f"property '{self._name}' has no setter")
raise AttributeError(f"property '{self.__name__}' has no setter")
self.fset(obj, value)

def __delete__(self, obj):
if self.fdel is None:
raise AttributeError(f"property '{self._name}' has no deleter")
raise AttributeError(f"property '{self.__name__}' has no deleter")
self.fdel(obj)

def getter(self, fget):
prop = type(self)(fget, self.fset, self.fdel, self.__doc__)
prop._name = self._name
prop.__name__ = self.__name__
return prop

def setter(self, fset):
prop = type(self)(self.fget, fset, self.fdel, self.__doc__)
prop._name = self._name
prop.__name__ = self.__name__
return prop

def deleter(self, fdel):
prop = type(self)(self.fget, self.fset, fdel, self.__doc__)
prop._name = self._name
prop.__name__ = self.__name__
return prop

.. testcode::
Expand Down
5 changes: 2 additions & 3 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,9 +825,8 @@ def _finddoc(obj):
cls = self.__class__
# Should be tested before isdatadescriptor().
elif isinstance(obj, property):
func = obj.fget
name = func.__name__
cls = _findclass(func)
name = obj.__name__
cls = _findclass(obj.fget)
if cls is None or getattr(cls, name) is not obj:
return None
elif ismethoddescriptor(obj) or isdatadescriptor(obj):
Expand Down
5 changes: 2 additions & 3 deletions Lib/pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,8 @@ def _finddoc(obj):
cls = self.__class__
# Should be tested before isdatadescriptor().
elif isinstance(obj, property):
func = obj.fget
name = func.__name__
cls = _findclass(func)
name = obj.__name__
cls = _findclass(obj.fget)
if cls is None or getattr(cls, name) is not obj:
return None
elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ def __doc__(cls):
return 'Second'
self.assertEqual(A.__doc__, 'Second')

def test_property_name(self):
def getter(self):
return 42

class A:
@property
def foo(self):
return 1

bar = property(getter)

self.assertEqual(A.foo.__name__, 'foo')
self.assertEqual(A.bar.__name__, 'bar')

A.baz = property(getter)
self.assertIsNone(A.baz.__name__)
A.baz.__name__ = 'mybaz'
self.assertEqual(A.baz.__name__, 'mybaz')
self.assertEqual(A.bar.__name__, 'bar') # not affected

def test_property_set_name_incorrect_args(self):
p = property()

Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,7 @@ def area(self):
return self.w * self.h

self.assertEqual(self._get_summary_lines(Rect.area), """\
area
Area of the rect
""")
self.assertIn("""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Expose ``__name__`` attribute on property.
5 changes: 5 additions & 0 deletions Objects/descrobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,10 @@ class property(object):
self.__set = fset
self.__del = fdel
self.__doc__ = doc
self.__name__ = None
def __set_name__(self, owner, name):
self.__name__ = name
def __get__(self, inst, type=None):
if inst is None:
Expand Down Expand Up @@ -1508,6 +1512,7 @@ static PyMemberDef property_members[] = {
{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
{"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY},
{"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), 0},
{"__name__", T_OBJECT, offsetof(propertyobject, prop_name), 0},
{0}
};

Expand Down

0 comments on commit 05db108

Please sign in to comment.