diff -r 353ced6ae182 Lib/inspect.py --- a/Lib/inspect.py Sun Sep 15 16:59:35 2013 -0700 +++ b/Lib/inspect.py Sun Sep 15 18:24:32 2013 -0700 @@ -34,20 +34,21 @@ Here are some of the useful functions pr import importlib.machinery import itertools import linecache import os import re import sys import tokenize import types import warnings import functools +import enum import builtins from operator import attrgetter from collections import namedtuple, OrderedDict # Create constants for the compiler flags in Include/code.h # We try to get them from dis to avoid duplication, but fall # back to hardcoding so the dependency is optional try: from dis import COMPILER_FLAG_NAMES as _flag_names except ImportError: @@ -260,35 +261,48 @@ def isabstract(object): return bool(isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT) def getmembers(object, predicate=None): """Return all members of an object as (name, value) pairs sorted by name. Optionally, only return members that satisfy a given predicate.""" if isclass(object): mro = (object,) + getmro(object) else: mro = () results = [] - for key in dir(object): + processed = set() + names = dir(object) + if isinstance(object, enum.EnumMeta): + # add any virtual attributes to the list of names + # this may result in duplicate entries if, for example, a virtual + # attribute with the same name as a member property exists + for base in object.__bases__: + for k, v in base.__dict__.items(): + if isinstance(v, enum._RouteClassAttributeToGetattr): + names.append(k) + for key in names: # First try to get the value via __dict__. Some descriptors don't # like calling their __get__ (see bug #1785). for base in mro: - if key in base.__dict__: + if key in base.__dict__ and key not in processed: + # handle the normal case first; if duplicate entries exist + # they will be handled second value = base.__dict__[key] break else: try: value = getattr(object, key) except AttributeError: continue if not predicate or predicate(value): results.append((key, value)) - results.sort() + processed.add(key) + results.sort(key=lambda pair: pair[0]) return results Attribute = namedtuple('Attribute', 'name kind defining_class object') def classify_class_attrs(cls): """Return list of attribute-descriptor tuples. For each name in dir(cls), the return list contains a 4-tuple with these elements: @@ -310,37 +324,49 @@ def classify_class_attrs(cls): info, like a __doc__ string. If one of the items in dir(cls) is stored in the metaclass it will now be discovered and not have None be listed as the class in which it was defined. """ mro = getmro(cls) metamro = getmro(type(cls)) # for attributes stored in the metaclass names = dir(cls) + if isinstance(cls, enum.EnumMeta): + # add any virtual attributes to the list of names + # this may result in duplicate entries if, for example, a virtual + # attribute with the same name as a member property exists + for base in cls.__bases__: + for k, v in base.__dict__.items(): + if isinstance(v, enum._RouteClassAttributeToGetattr): + names.append(k) result = [] + processed = set() for name in names: # Get the object associated with the name, and where it was defined. # Getting an obj from the __dict__ sometimes reveals more than # using getattr. Static and class methods are dramatic examples. # Furthermore, some objects may raise an Exception when fetched with # getattr(). This is the case with some descriptors (bug #1785). # Thus, we only use getattr() as a last resort. homecls = None for base in (cls,) + mro + metamro: - if name in base.__dict__: + if name in base.__dict__ and name not in processed: + # handle the normal case first; if duplicate entries exist + # they will be handled second obj = base.__dict__[name] homecls = base break else: obj = getattr(cls, name) homecls = getattr(obj, "__objclass__", homecls) + processed.add(name) # Classify the object. if isinstance(obj, staticmethod): kind = "static method" elif isinstance(obj, classmethod): kind = "class method" elif isinstance(obj, property): kind = "property" elif ismethoddescriptor(obj): kind = "method"