summaryrefslogtreecommitdiffstats
path: root/Lib/inspect.py
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2011-12-21 09:16:14 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2011-12-21 09:16:14 (GMT)
commitb8572a1673a8bf25adf3b054f76315bdac2ea45e (patch)
tree06f3a57e3f9b16698b1a3ed13a110c084b7ff51d /Lib/inspect.py
parent587c7381c76986d0ca51a574f249f06959b93f2c (diff)
downloadcpython-b8572a1673a8bf25adf3b054f76315bdac2ea45e.zip
cpython-b8572a1673a8bf25adf3b054f76315bdac2ea45e.tar.gz
cpython-b8572a1673a8bf25adf3b054f76315bdac2ea45e.tar.bz2
Issue #1785: Fix inspect and pydoc with misbehaving descriptors.
Also fixes issue #13581: `help(type)` wouldn't display anything.
Diffstat (limited to 'Lib/inspect.py')
-rw-r--r--Lib/inspect.py63
1 files changed, 36 insertions, 27 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index c727530..cb07b9d 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -247,12 +247,23 @@ def isabstract(object):
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):
- try:
- value = getattr(object, key)
- except AttributeError:
- continue
+ # 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__:
+ 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()
@@ -288,30 +299,21 @@ def classify_class_attrs(cls):
names = dir(cls)
result = []
for name in names:
- # Get the object associated with the name.
+ # 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.
- if name in cls.__dict__:
- obj = cls.__dict__[name]
+ # 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:
+ if name in base.__dict__:
+ obj = base.__dict__[name]
+ homecls = base
+ break
else:
obj = getattr(cls, name)
-
- # Figure out where it was defined.
- homecls = getattr(obj, "__objclass__", None)
- if homecls is None:
- # search the dicts.
- for base in mro:
- if name in base.__dict__:
- homecls = base
- break
-
- # Get the object again, in order to get it from the defining
- # __dict__ instead of via getattr (if possible).
- if homecls is not None and name in homecls.__dict__:
- obj = homecls.__dict__[name]
-
- # Also get the object via getattr.
- obj_via_getattr = getattr(cls, name)
+ homecls = getattr(obj, "__objclass__", homecls)
# Classify the object.
if isinstance(obj, staticmethod):
@@ -320,11 +322,18 @@ def classify_class_attrs(cls):
kind = "class method"
elif isinstance(obj, property):
kind = "property"
- elif (ismethod(obj_via_getattr) or
- ismethoddescriptor(obj_via_getattr)):
+ elif ismethoddescriptor(obj):
kind = "method"
- else:
+ elif isdatadescriptor(obj):
kind = "data"
+ else:
+ obj_via_getattr = getattr(cls, name)
+ if (ismethod(obj_via_getattr) or
+ ismethoddescriptor(obj_via_getattr)):
+ kind = "method"
+ else:
+ kind = "data"
+ obj = obj_via_getattr
result.append(Attribute(name, kind, homecls, obj))