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 e5ce6b7
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 8 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
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
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 e5ce6b7

Please sign in to comment.