diff options
author | Barry Warsaw <barry@python.org> | 2006-07-27 23:43:15 (GMT) |
---|---|---|
committer | Barry Warsaw <barry@python.org> | 2006-07-27 23:43:15 (GMT) |
commit | 00decd7835f0c2488451cedc345f2fb0650378b5 (patch) | |
tree | 88594aceb9e71f753a83234c242f8cedb40e2219 /Lib | |
parent | fc0e61d9b6f3c77f915fc8b4de287b3f8697e167 (diff) | |
download | cpython-00decd7835f0c2488451cedc345f2fb0650378b5.zip cpython-00decd7835f0c2488451cedc345f2fb0650378b5.tar.gz cpython-00decd7835f0c2488451cedc345f2fb0650378b5.tar.bz2 |
Patch #1520294: Support for getset and member descriptors in types.py,
inspect.py, and pydoc.py. Specifically, this allows for querying the type of
an object against these built-in C types and more importantly, for getting
their docstrings printed in the interactive interpreter's help() function.
This patch includes a new built-in module called _types which provides
definitions of getset and member descriptors for use by the types.py module.
These types are exposed as types.GetSetDescriptorType and
types.MemberDescriptorType. Query functions are provided as
inspect.isgetsetdescriptor() and inspect.ismemberdescriptor(). The
implementations of these are robust enough to work with Python implementations
other than CPython, which may not have these fundamental types.
The patch also includes documentation and test suite updates.
I commit these changes now under these guiding principles:
1. Silence is assent. The release manager has not said "no", and of the few
people that cared enough to respond to the thread, the worst vote was "0".
2. It's easier to ask for forgiveness than permission.
3. It's so dang easy to revert stuff in svn, that you could view this as a
forcing function. :)
Windows build patches will follow.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/inspect.py | 34 | ||||
-rwxr-xr-x | Lib/pydoc.py | 22 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 18 | ||||
-rw-r--r-- | Lib/types.py | 14 |
4 files changed, 83 insertions, 5 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py index dc2fa08..0cbf521 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -89,6 +89,40 @@ def isdatadescriptor(object): is not guaranteed.""" return (hasattr(object, "__set__") and hasattr(object, "__get__")) +if hasattr(types, 'MemberDescriptorType'): + # CPython and equivalent + def ismemberdescriptor(object): + """Return true if the object is a member descriptor. + + Member descriptors are specialized descriptors defined in extension + modules.""" + return isinstance(object, types.MemberDescriptorType) +else: + # Other implementations + def ismemberdescriptor(object): + """Return true if the object is a member descriptor. + + Member descriptors are specialized descriptors defined in extension + modules.""" + return False + +if hasattr(types, 'GetSetDescriptorType'): + # CPython and equivalent + def isgetsetdescriptor(object): + """Return true if the object is a getset descriptor. + + getset descriptors are specialized descriptors defined in extension + modules.""" + return isinstance(object, types.GetSetDescriptorType) +else: + # Other implementations + def isgetsetdescriptor(object): + """Return true if the object is a getset descriptor. + + getset descriptors are specialized descriptors defined in extension + modules.""" + return False + def isfunction(object): """Return true if the object is a user-defined function. diff --git a/Lib/pydoc.py b/Lib/pydoc.py index ff6e7ca..0fc624e 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -318,6 +318,8 @@ class Doc: # identifies something in a way that pydoc itself has issues handling; # think 'super' and how it is a descriptor (which raises the exception # by lacking a __name__ attribute) and an instance. + if inspect.isgetsetdescriptor(object): return self.docdata(*args) + if inspect.ismemberdescriptor(object): return self.docdata(*args) try: if inspect.ismodule(object): return self.docmodule(*args) if inspect.isclass(object): return self.docclass(*args) @@ -333,7 +335,7 @@ class Doc: name and ' ' + repr(name), type(object).__name__) raise TypeError, message - docmodule = docclass = docroutine = docother = fail + docmodule = docclass = docroutine = docother = docproperty = docdata = fail def getdocloc(self, object): """Return the location of module docs or None""" @@ -915,6 +917,10 @@ class HTMLDoc(Doc): lhs = name and '<strong>%s</strong> = ' % name or '' return lhs + self.repr(object) + def docdata(self, object, name=None, mod=None, cl=None): + """Produce html documentation for a data descriptor.""" + return self._docdescriptor(name, object, mod) + def index(self, dir, shadowed=None): """Generate an HTML index for a directory of modules.""" modpkgs = [] @@ -1268,6 +1274,10 @@ class TextDoc(Doc): """Produce text documentation for a property.""" return self._docdescriptor(name, object, mod) + def docdata(self, object, name=None, mod=None, cl=None): + """Produce text documentation for a data descriptor.""" + return self._docdescriptor(name, object, mod) + def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None): """Produce text documentation for a data object.""" repr = self.repr(object) @@ -1397,6 +1407,14 @@ def describe(thing): return 'module ' + thing.__name__ if inspect.isbuiltin(thing): return 'built-in function ' + thing.__name__ + if inspect.isgetsetdescriptor(thing): + return 'getset descriptor %s.%s.%s' % ( + thing.__objclass__.__module__, thing.__objclass__.__name__, + thing.__name__) + if inspect.ismemberdescriptor(thing): + return 'member descriptor %s.%s.%s' % ( + thing.__objclass__.__module__, thing.__objclass__.__name__, + thing.__name__) if inspect.isclass(thing): return 'class ' + thing.__name__ if inspect.isfunction(thing): @@ -1453,6 +1471,8 @@ def doc(thing, title='Python Library Documentation: %s', forceload=0): if not (inspect.ismodule(object) or inspect.isclass(object) or inspect.isroutine(object) or + inspect.isgetsetdescriptor(object) or + inspect.ismemberdescriptor(object) or isinstance(object, property)): # If the passed object is a piece of data or an instance, # document its available methods instead of its value. diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 76f2566..928af07 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1,6 +1,8 @@ import sys +import types import unittest import inspect +import datetime from test.test_support import TESTFN, run_unittest @@ -40,10 +42,11 @@ class IsTestBase(unittest.TestCase): self.failIf(other(obj), 'not %s(%s)' % (other.__name__, exp)) class TestPredicates(IsTestBase): - def test_eleven(self): - # Doc/lib/libinspect.tex claims there are 11 such functions + def test_thirteen(self): + # Doc/lib/libinspect.tex claims there are 13 such functions count = len(filter(lambda x:x.startswith('is'), dir(inspect))) - self.assertEqual(count, 11, "There are %d (not 11) is* functions" % count) + self.assertEqual(count, 13, + "There are %d (not 12) is* functions" % count) def test_excluding_predicates(self): self.istest(inspect.isbuiltin, 'sys.exit') @@ -58,6 +61,15 @@ class TestPredicates(IsTestBase): self.istest(inspect.istraceback, 'tb') self.istest(inspect.isdatadescriptor, '__builtin__.file.closed') self.istest(inspect.isdatadescriptor, '__builtin__.file.softspace') + if hasattr(types, 'GetSetDescriptorType'): + self.istest(inspect.isgetsetdescriptor, + 'type(tb.tb_frame).f_locals') + else: + self.failIf(inspect.isgetsetdescriptor(type(tb.tb_frame).f_locals)) + if hasattr(types, 'MemberDescriptorType'): + self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days') + else: + self.failIf(inspect.ismemberdescriptor(datetime.timedelta.days)) def test_isroutine(self): self.assert_(inspect.isroutine(mod.spam)) diff --git a/Lib/types.py b/Lib/types.py index 39812ac..6c8c2b2 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -86,4 +86,16 @@ EllipsisType = type(Ellipsis) DictProxyType = type(TypeType.__dict__) NotImplementedType = type(NotImplemented) -del sys, _f, _g, _C, _x # Not for export +# Extension types defined in a C helper module. XXX There may be no +# equivalent in implementations other than CPython, so it seems better to +# leave them undefined then to set them to e.g. None. +try: + import _types +except ImportError: + pass +else: + GetSetDescriptorType = type(_types.Helper.getter) + MemberDescriptorType = type(_types.Helper.member) + del _types + +del sys, _f, _g, _C, _x # Not for export |