Index: Doc/lib/libtypes.tex =================================================================== --- Doc/lib/libtypes.tex (revision 50608) +++ Doc/lib/libtypes.tex (working copy) @@ -180,6 +180,30 @@ \function{buffer()}\bifuncindex{buffer} function. \end{datadesc} +\begin{datadesc}{DictProxyType} +The type of dict proxies, such as \code{TypeType.__dict__}. +\end{datadesc} + +\begin{datadesc}{NotImplementedType} +The type of \code{NotImplemented} +\end{datadesc} + +\begin{datadesc}{GetSetType} +The type of objects defined in extension modules with \code{PyGetSetDef}, such +as \code{FrameType.f_locals} or \code{array.array.typecode}. This constant is +not defined in implementations of Python that do not have such extension +types, so for portable code use \code{hasattr(types, 'GetSetType')}. +\versionadded{2.5} +\end{datadesc} + +\begin{datadesc}{MemberDescriptorType} +The type of objects defined in extension modules with \code{PyMemberDef}, such +as \code {datetime.timedelta.days}. This constant is not defined in +implementations of Python that do not have such extension types, so for +portable code use \code{hasattr(types, 'MemberDescriptorType')}. +\versionadded{2.5} +\end{datadesc} + \begin{datadesc}{StringTypes} A sequence containing \code{StringType} and \code{UnicodeType} used to facilitate easier checking for any string object. Using this is more Index: Doc/lib/libinspect.tex =================================================================== --- Doc/lib/libinspect.tex (revision 50608) +++ Doc/lib/libinspect.tex (working copy) @@ -180,13 +180,32 @@ Return true if the object is a data descriptor. Data descriptors have both a __get__ and a __set__ attribute. Examples are - properties (defined in Python) and getsets and members (defined in C). - Typically, data descriptors will also have __name__ and __doc__ attributes - (properties, getsets, and members have both of these attributes), but this - is not guaranteed. + properties (defined in Python), getsets, and members. The latter two are + defined in C and there are more specific tests available for those types, + which is robust across Python implementations. Typically, data descriptors + will also have __name__ and __doc__ attributes (properties, getsets, and + members have both of these attributes), but this is not guaranteed. \versionadded{2.3} \end{funcdesc} +\begin{funcdesc}{isgetset}{object} + Return true if the object is a getset descriptor. + + getsets are attributes defined in extension modules via \code{PyGetSetDef} + structures. For Python implementations without such types, this method will + always return false. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{ismemberdescriptor}{object} + Return true if the object is a member descriptor. + + Member descriptors are attributes defined in extension modules via + \code{PyMemberDef} structures. For Python implementations without such + types, this method will always return false. +\versionadded{2.5} +\end{funcdesc} + \subsection{Retrieving source code \label{inspect-source}} Index: Lib/inspect.py =================================================================== --- Lib/inspect.py (revision 50608) +++ Lib/inspect.py (working copy) @@ -89,6 +89,40 @@ 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, 'GetSetType'): + # CPython and equivalent + def isgetset(object): + """Return true if the object is a getset descriptor. + + getset descriptors are specialized descriptors defined in extension + modules.""" + return isinstance(object, types.GetSetType) +else: + # Other implementations + def isgetset(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. Index: Lib/pydoc.py =================================================================== --- Lib/pydoc.py (revision 50608) +++ Lib/pydoc.py (working copy) @@ -318,6 +318,8 @@ # 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.isgetset(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 @@ 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 @@ lhs = name and '%s = ' % 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 @@ """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 @@ return 'module ' + thing.__name__ if inspect.isbuiltin(thing): return 'built-in function ' + thing.__name__ + if inspect.isgetset(thing): + return 'data 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 @@ if not (inspect.ismodule(object) or inspect.isclass(object) or inspect.isroutine(object) or + inspect.isgetset(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. Index: Lib/types.py =================================================================== --- Lib/types.py (revision 50608) +++ Lib/types.py (working copy) @@ -86,4 +86,15 @@ 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: + GetSetType = type(_types.Helper.getter) + MemberDescriptorType = type(_types.Helper.member) + +del sys, _f, _g, _C, _x, _types # Not for export Index: Lib/test/test_inspect.py =================================================================== --- Lib/test/test_inspect.py (revision 50608) +++ Lib/test/test_inspect.py (working copy) @@ -1,6 +1,7 @@ import sys import unittest import inspect +import datetime from test.test_support import TESTFN, run_unittest @@ -40,10 +41,11 @@ self.failIf(other(obj), 'not %s(%s)' % (other.__name__, exp)) class TestPredicates(IsTestBase): - def test_eleven(self): + def test_twelve(self): # Doc/lib/libinspect.tex claims there are 11 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 +60,8 @@ self.istest(inspect.istraceback, 'tb') self.istest(inspect.isdatadescriptor, '__builtin__.file.closed') self.istest(inspect.isdatadescriptor, '__builtin__.file.softspace') + self.istest(inspect.isgetset, 'type(tb.tb_frame).f_locals') + self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days') def test_isroutine(self): self.assert_(inspect.isroutine(mod.spam)) Index: Makefile.pre.in =================================================================== --- Makefile.pre.in (revision 50608) +++ Makefile.pre.in (working copy) @@ -317,6 +317,7 @@ ########################################################################## # objects that get linked into the Python library LIBRARY_OBJS= \ + Modules/_typesmodule.o \ Modules/getbuildinfo.o \ $(PARSER_OBJS) \ $(OBJECT_OBJS) \ @@ -353,6 +354,7 @@ $(LIBRARY): $(LIBRARY_OBJS) -rm -f $@ $(AR) cr $@ Modules/getbuildinfo.o + $(AR) cr $@ Modules/_typesmodule.o $(AR) cr $@ $(PARSER_OBJS) $(AR) cr $@ $(OBJECT_OBJS) $(AR) cr $@ $(PYTHON_OBJS) @@ -485,7 +487,7 @@ $(AST_C): $(AST_ASDL) $(ASDLGEN_FILES) $(ASDLGEN) -c $(AST_C_DIR) $(AST_ASDL) - + Python/compile.o Python/symtable.o: $(GRAMMAR_H) $(AST_H) Python/getplatform.o: $(srcdir)/Python/getplatform.c Index: Modules/_typesmodule.c =================================================================== --- Modules/_typesmodule.c (revision 0) +++ Modules/_typesmodule.c (revision 0) @@ -0,0 +1,94 @@ +/* This extension module exposes some types that are only available at the + * C level. It should not be used directly, but instead through the Python + * level types modules, which imports this. + */ + +#include "Python.h" +#include "structmember.h" + +typedef struct +{ + PyObject_HEAD; + int member; +} Helper; + +static PyMemberDef helper_members[] = { + { "member", T_INT, offsetof(Helper, member), READONLY, + PyDoc_STR("A member descriptor") + }, + { NULL } +}; + +static PyObject * +helper_getter(Helper *self, void *unused) +{ + Py_RETURN_NONE; +} + +static PyGetSetDef helper_getset[] = { + { "getter", (getter)helper_getter, NULL, + PyDoc_STR("A getset descriptor"), + }, + { NULL } +}; + +static PyTypeObject HelperType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "_types.Helper", /* tp_name */ + sizeof(Helper), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + helper_members, /* tp_members */ + helper_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ +}; + +PyMODINIT_FUNC +init_types(void) +{ + PyObject *m; + + m = Py_InitModule3("_types", NULL, "A types module helper"); + if (!m) + return; + + if (PyType_Ready(&HelperType) < 0) + return; + + Py_INCREF(&HelperType); + PyModule_AddObject(m, "Helper", (PyObject *)&HelperType); +} + + Index: Modules/config.c.in =================================================================== --- Modules/config.c.in (revision 50608) +++ Modules/config.c.in (working copy) @@ -28,6 +28,7 @@ extern void initimp(void); extern void initgc(void); extern void init_ast(void); +extern void init_types(void); struct _inittab _PyImport_Inittab[] = { @@ -42,6 +43,9 @@ /* This lives in Python/Python-ast.c */ {"_ast", init_ast}, + /* This lives in Python/_types.c */ + {"_types", init_types}, + /* These entries are here for sys.builtin_module_names */ {"__main__", NULL}, {"__builtin__", NULL},