mirror of https://github.com/python/cpython
Backport from Py3k branch:
Patch #1591665: implement the __dir__() special function lookup in PyObject_Dir. Had to change a few bits of the patch because classobjs and __methods__ are still in Py2.6.
This commit is contained in:
parent
2681beb23e
commit
871f1bc601
|
@ -274,21 +274,34 @@ class C:
|
|||
\end{funcdesc}
|
||||
|
||||
\begin{funcdesc}{dir}{\optional{object}}
|
||||
Without arguments, return the list of names in the current local
|
||||
symbol table. With an argument, attempts to return a list of valid
|
||||
attributes for that object. This information is gleaned from the
|
||||
object's \member{__dict__} attribute, if defined, and from the class
|
||||
or type object. The list is not necessarily complete.
|
||||
If the object is a module object, the list contains the names of the
|
||||
Without arguments, return the list of names in the current local scope. With
|
||||
an argument, attempt to return a list of valid attributes for that object.
|
||||
|
||||
If the object has a method named \method{__dir__()}, this method will be
|
||||
called and must return the list of attributes. This allows objects that
|
||||
implement a custom \function{__getattr__()} or \function{__getattribute__()}
|
||||
function to customize the way \function{dir()} reports their attributes.
|
||||
|
||||
If the object does not provide \method{__dir__()}, the function tries its best
|
||||
to gather information from the object's \member{__dict__} attribute, if
|
||||
defined, and from its type object. The resulting list is not necessarily
|
||||
complete, and may be inaccurate when the object has a custom
|
||||
\function{__getattr__()}.
|
||||
|
||||
The default \function{dir()} mechanism behaves differently with different
|
||||
types of objects, as it attempts to produce the most relevant, rather than
|
||||
complete, information:
|
||||
\begin{itemize}
|
||||
\item If the object is a module object, the list contains the names of the
|
||||
module's attributes.
|
||||
If the object is a type or class object,
|
||||
the list contains the names of its attributes,
|
||||
and recursively of the attributes of its bases.
|
||||
Otherwise, the list contains the object's attributes' names,
|
||||
the names of its class's attributes,
|
||||
and recursively of the attributes of its class's base classes.
|
||||
The resulting list is sorted alphabetically.
|
||||
For example:
|
||||
\item If the object is a type or class object, the list contains the names of
|
||||
its attributes, and recursively of the attributes of its bases.
|
||||
\item Otherwise, the list contains the object's attributes' names, the names
|
||||
of its class's attributes, and recursively of the attributes of its class's
|
||||
base classes.
|
||||
\end{itemize}
|
||||
|
||||
The resulting list is sorted alphabetically. For example:
|
||||
|
||||
\begin{verbatim}
|
||||
>>> import struct
|
||||
|
@ -296,13 +309,19 @@ class C:
|
|||
['__builtins__', '__doc__', '__name__', 'struct']
|
||||
>>> dir(struct)
|
||||
['__doc__', '__name__', 'calcsize', 'error', 'pack', 'unpack']
|
||||
>>> class Foo(object):
|
||||
... def __dir__(self):
|
||||
... return ["kan", "ga", "roo"]
|
||||
...
|
||||
>>> f = Foo()
|
||||
>>> dir(f)
|
||||
['ga', 'kan', 'roo']
|
||||
\end{verbatim}
|
||||
|
||||
\note{Because \function{dir()} is supplied primarily as a convenience
|
||||
for use at an interactive prompt,
|
||||
it tries to supply an interesting set of names more than it tries to
|
||||
supply a rigorously or consistently defined set of names,
|
||||
and its detailed behavior may change across releases.}
|
||||
\note{Because \function{dir()} is supplied primarily as a convenience for use
|
||||
at an interactive prompt, it tries to supply an interesting set of names
|
||||
more than it tries to supply a rigorously or consistently defined set of
|
||||
names, and its detailed behavior may change across releases.}
|
||||
\end{funcdesc}
|
||||
|
||||
\begin{funcdesc}{divmod}{a, b}
|
||||
|
|
|
@ -259,12 +259,67 @@ class BuiltinTest(unittest.TestCase):
|
|||
self.assertRaises(TypeError, delattr)
|
||||
|
||||
def test_dir(self):
|
||||
x = 1
|
||||
self.assert_('x' in dir())
|
||||
import sys
|
||||
self.assert_('modules' in dir(sys))
|
||||
# dir(wrong number of arguments)
|
||||
self.assertRaises(TypeError, dir, 42, 42)
|
||||
|
||||
# dir() - local scope
|
||||
local_var = 1
|
||||
self.assert_('local_var' in dir())
|
||||
|
||||
# dir(module)
|
||||
import sys
|
||||
self.assert_('exit' in dir(sys))
|
||||
|
||||
# dir(module_with_invalid__dict__)
|
||||
import types
|
||||
class Foo(types.ModuleType):
|
||||
__dict__ = 8
|
||||
f = Foo("foo")
|
||||
self.assertRaises(TypeError, dir, f)
|
||||
|
||||
# dir(type)
|
||||
self.assert_("strip" in dir(str))
|
||||
self.assert_("__mro__" not in dir(str))
|
||||
|
||||
# dir(obj)
|
||||
class Foo(object):
|
||||
def __init__(self):
|
||||
self.x = 7
|
||||
self.y = 8
|
||||
self.z = 9
|
||||
f = Foo()
|
||||
self.assert_("y" in dir(f))
|
||||
|
||||
# dir(obj_no__dict__)
|
||||
class Foo(object):
|
||||
__slots__ = []
|
||||
f = Foo()
|
||||
self.assert_("__repr__" in dir(f))
|
||||
|
||||
# dir(obj_no__class__with__dict__)
|
||||
# (an ugly trick to cause getattr(f, "__class__") to fail)
|
||||
class Foo(object):
|
||||
__slots__ = ["__class__", "__dict__"]
|
||||
def __init__(self):
|
||||
self.bar = "wow"
|
||||
f = Foo()
|
||||
self.assert_("__repr__" not in dir(f))
|
||||
self.assert_("bar" in dir(f))
|
||||
|
||||
# dir(obj_using __dir__)
|
||||
class Foo(object):
|
||||
def __dir__(self):
|
||||
return ["kan", "ga", "roo"]
|
||||
f = Foo()
|
||||
self.assert_(dir(f) == ["ga", "kan", "roo"])
|
||||
|
||||
# dir(obj__dir__not_list)
|
||||
class Foo(object):
|
||||
def __dir__(self):
|
||||
return 7
|
||||
f = Foo()
|
||||
self.assertRaises(TypeError, dir, f)
|
||||
|
||||
def test_divmod(self):
|
||||
self.assertEqual(divmod(12, 7), (1, 5))
|
||||
self.assertEqual(divmod(-12, 7), (-2, 2))
|
||||
|
|
|
@ -12,6 +12,10 @@ What's New in Python 2.6 alpha 1?
|
|||
Core and builtins
|
||||
-----------------
|
||||
|
||||
- The dir() function has been extended to call the __dir__() method on
|
||||
its argument, if it exists. If not, it will work like before. This allows
|
||||
customizing the output of dir() in the presence of a __getattr__().
|
||||
|
||||
- Patch #1675981: remove unreachable code from ``type.__new__()`` method.
|
||||
|
||||
- Patch #1491866: change the complex() constructor to allow parthensized
|
||||
|
|
241
Objects/object.c
241
Objects/object.c
|
@ -1566,6 +1566,8 @@ PyCallable_Check(PyObject *x)
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------- PyObject_Dir() helpers ------------------------- */
|
||||
|
||||
/* Helper for PyObject_Dir.
|
||||
Merge the __dict__ of aclass into dict, and recursively also all
|
||||
the __dict__s of aclass's base classes. The order of merging isn't
|
||||
|
@ -1662,121 +1664,192 @@ merge_list_attr(PyObject* dict, PyObject* obj, const char *attrname)
|
|||
return result;
|
||||
}
|
||||
|
||||
/* Like __builtin__.dir(arg). See bltinmodule.c's builtin_dir for the
|
||||
docstring, which should be kept in synch with this implementation. */
|
||||
|
||||
PyObject *
|
||||
PyObject_Dir(PyObject *arg)
|
||||
/* Helper for PyObject_Dir without arguments: returns the local scope. */
|
||||
static PyObject *
|
||||
_dir_locals()
|
||||
{
|
||||
/* Set exactly one of these non-NULL before the end. */
|
||||
PyObject *result = NULL; /* result list */
|
||||
PyObject *masterdict = NULL; /* result is masterdict.keys() */
|
||||
|
||||
/* If NULL arg, return the locals. */
|
||||
if (arg == NULL) {
|
||||
PyObject *names;
|
||||
PyObject *locals = PyEval_GetLocals();
|
||||
if (locals == NULL)
|
||||
goto error;
|
||||
result = PyMapping_Keys(locals);
|
||||
if (result == NULL)
|
||||
goto error;
|
||||
|
||||
if (locals == NULL) {
|
||||
PyErr_SetString(PyExc_SystemError, "frame does not exist");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Elif this is some form of module, we only want its dict. */
|
||||
else if (PyModule_Check(arg)) {
|
||||
masterdict = PyObject_GetAttrString(arg, "__dict__");
|
||||
if (masterdict == NULL)
|
||||
goto error;
|
||||
if (!PyDict_Check(masterdict)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"module.__dict__ is not a dictionary");
|
||||
goto error;
|
||||
}
|
||||
names = PyMapping_Keys(locals);
|
||||
if (!names)
|
||||
return NULL;
|
||||
if (!PyList_Check(names)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"dir(): expected keys() of locals to be a list, "
|
||||
"not '%.200s'", names->ob_type->tp_name);
|
||||
Py_DECREF(names);
|
||||
return NULL;
|
||||
}
|
||||
/* the locals don't need to be DECREF'd */
|
||||
return names;
|
||||
}
|
||||
|
||||
/* Elif some form of type or class, grab its dict and its bases.
|
||||
We deliberately don't suck up its __class__, as methods belonging
|
||||
to the metaclass would probably be more confusing than helpful. */
|
||||
else if (PyType_Check(arg) || PyClass_Check(arg)) {
|
||||
masterdict = PyDict_New();
|
||||
if (masterdict == NULL)
|
||||
goto error;
|
||||
if (merge_class_dict(masterdict, arg) < 0)
|
||||
goto error;
|
||||
}
|
||||
/* Helper for PyObject_Dir of type objects: returns __dict__ and __bases__.
|
||||
We deliberately don't suck up its __class__, as methods belonging to the
|
||||
metaclass would probably be more confusing than helpful.
|
||||
*/
|
||||
static PyObject *
|
||||
_specialized_dir_type(PyObject *obj)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
PyObject *dict = PyDict_New();
|
||||
|
||||
/* Else look at its dict, and the attrs reachable from its class. */
|
||||
if (dict != NULL && merge_class_dict(dict, obj) == 0)
|
||||
result = PyDict_Keys(dict);
|
||||
|
||||
Py_XDECREF(dict);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Helper for PyObject_Dir of module objects: returns the module's __dict__. */
|
||||
static PyObject *
|
||||
_specialized_dir_module(PyObject *obj)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
PyObject *dict = PyObject_GetAttrString(obj, "__dict__");
|
||||
|
||||
if (dict != NULL) {
|
||||
if (PyDict_Check(dict))
|
||||
result = PyDict_Keys(dict);
|
||||
else {
|
||||
PyObject *itsclass;
|
||||
/* Create a dict to start with. CAUTION: Not everything
|
||||
responding to __dict__ returns a dict! */
|
||||
masterdict = PyObject_GetAttrString(arg, "__dict__");
|
||||
if (masterdict == NULL) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s.__dict__ is not a dictionary",
|
||||
PyModule_GetName(obj));
|
||||
}
|
||||
}
|
||||
|
||||
Py_XDECREF(dict);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Helper for PyObject_Dir of generic objects: returns __dict__, __class__,
|
||||
and recursively up the __class__.__bases__ chain.
|
||||
*/
|
||||
static PyObject *
|
||||
_generic_dir(PyObject *obj)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
PyObject *dict = NULL;
|
||||
PyObject *itsclass = NULL;
|
||||
|
||||
/* Get __dict__ (which may or may not be a real dict...) */
|
||||
dict = PyObject_GetAttrString(obj, "__dict__");
|
||||
if (dict == NULL) {
|
||||
PyErr_Clear();
|
||||
masterdict = PyDict_New();
|
||||
dict = PyDict_New();
|
||||
}
|
||||
else if (!PyDict_Check(masterdict)) {
|
||||
Py_DECREF(masterdict);
|
||||
masterdict = PyDict_New();
|
||||
else if (!PyDict_Check(dict)) {
|
||||
Py_DECREF(dict);
|
||||
dict = PyDict_New();
|
||||
}
|
||||
else {
|
||||
/* The object may have returned a reference to its
|
||||
dict, so copy it to avoid mutating it. */
|
||||
PyObject *temp = PyDict_Copy(masterdict);
|
||||
Py_DECREF(masterdict);
|
||||
masterdict = temp;
|
||||
/* Copy __dict__ to avoid mutating it. */
|
||||
PyObject *temp = PyDict_Copy(dict);
|
||||
Py_DECREF(dict);
|
||||
dict = temp;
|
||||
}
|
||||
if (masterdict == NULL)
|
||||
|
||||
if (dict == NULL)
|
||||
goto error;
|
||||
|
||||
/* Merge in __members__ and __methods__ (if any).
|
||||
XXX Would like this to go away someday; for now, it's
|
||||
XXX needed to get at im_self etc of method objects. */
|
||||
if (merge_list_attr(masterdict, arg, "__members__") < 0)
|
||||
* This is removed in Python 3000. */
|
||||
if (merge_list_attr(dict, obj, "__members__") < 0)
|
||||
goto error;
|
||||
if (merge_list_attr(masterdict, arg, "__methods__") < 0)
|
||||
if (merge_list_attr(dict, obj, "__methods__") < 0)
|
||||
goto error;
|
||||
|
||||
/* Merge in attrs reachable from its class.
|
||||
CAUTION: Not all objects have a __class__ attr. */
|
||||
itsclass = PyObject_GetAttrString(arg, "__class__");
|
||||
/* Merge in attrs reachable from its class. */
|
||||
itsclass = PyObject_GetAttrString(obj, "__class__");
|
||||
if (itsclass == NULL)
|
||||
/* XXX(tomer): Perhaps fall back to obj->ob_type if no
|
||||
__class__ exists? */
|
||||
PyErr_Clear();
|
||||
else {
|
||||
int status = merge_class_dict(masterdict, itsclass);
|
||||
Py_DECREF(itsclass);
|
||||
if (status < 0)
|
||||
if (merge_class_dict(dict, itsclass) != 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
assert((result == NULL) ^ (masterdict == NULL));
|
||||
if (masterdict != NULL) {
|
||||
/* The result comes from its keys. */
|
||||
assert(result == NULL);
|
||||
result = PyDict_Keys(masterdict);
|
||||
result = PyDict_Keys(dict);
|
||||
/* fall through */
|
||||
error:
|
||||
Py_XDECREF(itsclass);
|
||||
Py_XDECREF(dict);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Helper for PyObject_Dir: object introspection.
|
||||
This calls one of the above specialized versions if no __dir__ method
|
||||
exists. */
|
||||
static PyObject *
|
||||
_dir_object(PyObject *obj)
|
||||
{
|
||||
PyObject * result = NULL;
|
||||
PyObject * dirfunc = PyObject_GetAttrString((PyObject*)obj->ob_type,
|
||||
"__dir__");
|
||||
|
||||
assert(obj);
|
||||
if (dirfunc == NULL) {
|
||||
/* use default implementation */
|
||||
PyErr_Clear();
|
||||
if (PyModule_Check(obj))
|
||||
result = _specialized_dir_module(obj);
|
||||
else if (PyType_Check(obj) || PyClass_Check(obj))
|
||||
result = _specialized_dir_type(obj);
|
||||
else
|
||||
result = _generic_dir(obj);
|
||||
}
|
||||
else {
|
||||
/* use __dir__ */
|
||||
result = PyObject_CallFunctionObjArgs(dirfunc, obj, NULL);
|
||||
Py_DECREF(dirfunc);
|
||||
if (result == NULL)
|
||||
goto error;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
assert(result);
|
||||
/* result must be a list */
|
||||
/* XXX(gbrandl): could also check if all items are strings */
|
||||
if (!PyList_Check(result)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Expected keys() to be a list, not '%.200s'",
|
||||
"__dir__() must return a list, not %.200s",
|
||||
result->ob_type->tp_name);
|
||||
goto error;
|
||||
}
|
||||
if (PyList_Sort(result) != 0)
|
||||
goto error;
|
||||
else
|
||||
goto normal_return;
|
||||
|
||||
error:
|
||||
Py_XDECREF(result);
|
||||
Py_DECREF(result);
|
||||
result = NULL;
|
||||
/* fall through */
|
||||
normal_return:
|
||||
Py_XDECREF(masterdict);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Implementation of dir() -- if obj is NULL, returns the names in the current
|
||||
(local) scope. Otherwise, performs introspection of the object: returns a
|
||||
sorted list of attribute names (supposedly) accessible from the object
|
||||
*/
|
||||
PyObject *
|
||||
PyObject_Dir(PyObject *obj)
|
||||
{
|
||||
PyObject * result;
|
||||
|
||||
if (obj == NULL)
|
||||
/* no object -- introspect the locals */
|
||||
result = _dir_locals();
|
||||
else
|
||||
/* object -- introspect the object */
|
||||
result = _dir_object(obj);
|
||||
|
||||
assert(result == NULL || PyList_Check(result));
|
||||
|
||||
if (result != NULL && PyList_Sort(result) != 0) {
|
||||
/* sorting the list failed */
|
||||
Py_DECREF(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -495,15 +495,16 @@ builtin_dir(PyObject *self, PyObject *args)
|
|||
PyDoc_STRVAR(dir_doc,
|
||||
"dir([object]) -> list of strings\n"
|
||||
"\n"
|
||||
"Return an alphabetized list of names comprising (some of) the attributes\n"
|
||||
"of the given object, and of attributes reachable from it:\n"
|
||||
"\n"
|
||||
"No argument: the names in the current scope.\n"
|
||||
"Module object: the module attributes.\n"
|
||||
"Type or class object: its attributes, and recursively the attributes of\n"
|
||||
" its bases.\n"
|
||||
"Otherwise: its attributes, its class's attributes, and recursively the\n"
|
||||
" attributes of its class's base classes.");
|
||||
"If called without an argument, return the names in the current scope.\n"
|
||||
"Else, return an alphabetized list of names comprising (some of) the attributes\n"
|
||||
"of the given object, and of attributes reachable from it.\n"
|
||||
"If the object supplies a method named __dir__, it will be used; otherwise\n"
|
||||
"the default dir() logic is used and returns:\n"
|
||||
" for a module object: the module's attributes.\n"
|
||||
" for a class object: its attributes, and recursively the attributes\n"
|
||||
" of its bases.\n"
|
||||
" for an other object: its attributes, its class's attributes, and\n"
|
||||
" recursively the attributes of its class's base classes.");
|
||||
|
||||
static PyObject *
|
||||
builtin_divmod(PyObject *self, PyObject *args)
|
||||
|
|
Loading…
Reference in New Issue