mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
Close #19030: improvements to inspect and Enum.
inspect.getmembers and inspect.classify_class_attrs now search the metaclass mro for types.DynamicClassAttributes (what use to be called enum._RouteClassAttributeToGetattr); in part this means that these two functions no longer rely solely on dir(). Besides now returning more accurate information, these improvements also allow a more helpful help() on Enum classes.
This commit is contained in:
parent
7cba5fd267
commit
e03ea37a7b
4 changed files with 155 additions and 60 deletions
57
Lib/types.py
57
Lib/types.py
|
@ -99,3 +99,60 @@ def _calculate_meta(meta, bases):
|
|||
"must be a (non-strict) subclass "
|
||||
"of the metaclasses of all its bases")
|
||||
return winner
|
||||
|
||||
class DynamicClassAttribute:
|
||||
"""Route attribute access on a class to __getattr__.
|
||||
|
||||
This is a descriptor, used to define attributes that act differently when
|
||||
accessed through an instance and through a class. Instance access remains
|
||||
normal, but access to an attribute through a class will be routed to the
|
||||
class's __getattr__ method; this is done by raising AttributeError.
|
||||
|
||||
This allows one to have properties active on an instance, and have virtual
|
||||
attributes on the class with the same name (see Enum for an example).
|
||||
|
||||
"""
|
||||
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
|
||||
self.fget = fget
|
||||
self.fset = fset
|
||||
self.fdel = fdel
|
||||
# next two lines make DynamicClassAttribute act the same as property
|
||||
self.__doc__ = doc or fget.__doc__ or self.__doc__
|
||||
self.overwrite_doc = doc is None
|
||||
# support for abstract methods
|
||||
self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
|
||||
|
||||
def __get__(self, instance, ownerclass=None):
|
||||
if instance is None:
|
||||
if self.__isabstractmethod__:
|
||||
return self
|
||||
raise AttributeError()
|
||||
elif self.fget is None:
|
||||
raise AttributeError("unreadable attribute")
|
||||
return self.fget(instance)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if self.fset is None:
|
||||
raise AttributeError("can't set attribute")
|
||||
self.fset(instance, value)
|
||||
|
||||
def __delete__(self, instance):
|
||||
if self.fdel is None:
|
||||
raise AttributeError("can't delete attribute")
|
||||
self.fdel(instance)
|
||||
|
||||
def getter(self, fget):
|
||||
fdoc = fget.__doc__ if self.overwrite_doc else None
|
||||
result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
|
||||
result.overwrite_doc = self.overwrite_doc
|
||||
return result
|
||||
|
||||
def setter(self, fset):
|
||||
result = type(self)(self.fget, fset, self.fdel, self.__doc__)
|
||||
result.overwrite_doc = self.overwrite_doc
|
||||
return result
|
||||
|
||||
def deleter(self, fdel):
|
||||
result = type(self)(self.fget, self.fset, fdel, self.__doc__)
|
||||
result.overwrite_doc = self.overwrite_doc
|
||||
return result
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue