aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/types.py
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2013-09-25 07:14:41 -0700
committerEthan Furman <ethan@stoneleaf.us>2013-09-25 07:14:41 -0700
commite03ea37a7bea48c46e6d96851f471db0f3c8e6e2 (patch)
tree7b3ac5ca8ed5b94a29a80400e24746788c0b9efb /Lib/types.py
parent7cba5fd267219d23aec97fad91d73bea77daf5e2 (diff)
downloadcpython-e03ea37a7bea48c46e6d96851f471db0f3c8e6e2.tar.gz
cpython-e03ea37a7bea48c46e6d96851f471db0f3c8e6e2.zip
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.
Diffstat (limited to 'Lib/types.py')
-rw-r--r--Lib/types.py57
1 files changed, 57 insertions, 0 deletions
diff --git a/Lib/types.py b/Lib/types.py
index cfd09eaaffe..b0bbfc1b4a0 100644
--- a/Lib/types.py
+++ b/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