diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2013-09-25 07:14:41 -0700 |
---|---|---|
committer | Ethan Furman <ethan@stoneleaf.us> | 2013-09-25 07:14:41 -0700 |
commit | e03ea37a7bea48c46e6d96851f471db0f3c8e6e2 (patch) | |
tree | 7b3ac5ca8ed5b94a29a80400e24746788c0b9efb /Lib/types.py | |
parent | 7cba5fd267219d23aec97fad91d73bea77daf5e2 (diff) | |
download | cpython-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.py | 57 |
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 |