mirror of https://github.com/python/cpython
move specialized dir implementations into __dir__ methods (closes #12166)
This commit is contained in:
parent
9bcfacd4fc
commit
82b00c1d30
|
@ -170,6 +170,7 @@ You can get the information from the list type:
|
||||||
'__contains__',
|
'__contains__',
|
||||||
'__delattr__',
|
'__delattr__',
|
||||||
'__delitem__',
|
'__delitem__',
|
||||||
|
'__dir__',
|
||||||
'__doc__',
|
'__doc__',
|
||||||
'__eq__',
|
'__eq__',
|
||||||
'__format__',
|
'__format__',
|
||||||
|
|
|
@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #12166: Move implementations of dir() specialized for various types into
|
||||||
|
the __dir__() methods of those types.
|
||||||
|
|
||||||
- Correct lookup of __dir__ on objects. Among other things, this causes errors
|
- Correct lookup of __dir__ on objects. Among other things, this causes errors
|
||||||
besides AttributeError found on lookup to be propagated.
|
besides AttributeError found on lookup to be propagated.
|
||||||
|
|
||||||
|
|
|
@ -413,6 +413,34 @@ module_clear(PyModuleObject *m)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
module_dir(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *result = NULL;
|
||||||
|
PyObject *dict = PyObject_GetAttrString(self, "__dict__");
|
||||||
|
|
||||||
|
if (dict != NULL) {
|
||||||
|
if (PyDict_Check(dict))
|
||||||
|
result = PyDict_Keys(dict);
|
||||||
|
else {
|
||||||
|
const char *name = PyModule_GetName(self);
|
||||||
|
if (name)
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"%.200s.__dict__ is not a dictionary",
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(dict);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
{"__dir__", module_dir, METH_NOARGS,
|
||||||
|
PyDoc_STR("__dir__() -> specialized dir() implementation")},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(module_doc,
|
PyDoc_STRVAR(module_doc,
|
||||||
"module(name[, doc])\n\
|
"module(name[, doc])\n\
|
||||||
|
@ -449,7 +477,7 @@ PyTypeObject PyModule_Type = {
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
0, /* tp_methods */
|
module_methods, /* tp_methods */
|
||||||
module_members, /* tp_members */
|
module_members, /* tp_members */
|
||||||
0, /* tp_getset */
|
0, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
|
|
167
Objects/object.c
167
Objects/object.c
|
@ -1182,66 +1182,6 @@ PyCallable_Check(PyObject *x)
|
||||||
return x->ob_type->tp_call != NULL;
|
return x->ob_type->tp_call != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------- 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
|
|
||||||
defined, as it's expected that only the final set of dict keys is
|
|
||||||
interesting.
|
|
||||||
Return 0 on success, -1 on error.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
merge_class_dict(PyObject* dict, PyObject* aclass)
|
|
||||||
{
|
|
||||||
PyObject *classdict;
|
|
||||||
PyObject *bases;
|
|
||||||
|
|
||||||
assert(PyDict_Check(dict));
|
|
||||||
assert(aclass);
|
|
||||||
|
|
||||||
/* Merge in the type's dict (if any). */
|
|
||||||
classdict = PyObject_GetAttrString(aclass, "__dict__");
|
|
||||||
if (classdict == NULL)
|
|
||||||
PyErr_Clear();
|
|
||||||
else {
|
|
||||||
int status = PyDict_Update(dict, classdict);
|
|
||||||
Py_DECREF(classdict);
|
|
||||||
if (status < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively merge in the base types' (if any) dicts. */
|
|
||||||
bases = PyObject_GetAttrString(aclass, "__bases__");
|
|
||||||
if (bases == NULL)
|
|
||||||
PyErr_Clear();
|
|
||||||
else {
|
|
||||||
/* We have no guarantee that bases is a real tuple */
|
|
||||||
Py_ssize_t i, n;
|
|
||||||
n = PySequence_Size(bases); /* This better be right */
|
|
||||||
if (n < 0)
|
|
||||||
PyErr_Clear();
|
|
||||||
else {
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
int status;
|
|
||||||
PyObject *base = PySequence_GetItem(bases, i);
|
|
||||||
if (base == NULL) {
|
|
||||||
Py_DECREF(bases);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
status = merge_class_dict(dict, base);
|
|
||||||
Py_DECREF(base);
|
|
||||||
if (status < 0) {
|
|
||||||
Py_DECREF(bases);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Py_DECREF(bases);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper for PyObject_Dir without arguments: returns the local scope. */
|
/* Helper for PyObject_Dir without arguments: returns the local scope. */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1269,118 +1209,20 @@ _dir_locals(void)
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper for PyObject_Dir of type objects: returns __dict__ and __bases__.
|
/* Helper for PyObject_Dir: object introspection. */
|
||||||
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();
|
|
||||||
|
|
||||||
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 {
|
|
||||||
const char *name = PyModule_GetName(obj);
|
|
||||||
if (name)
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"%.200s.__dict__ is not a dictionary",
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
dict = PyDict_New();
|
|
||||||
}
|
|
||||||
else if (!PyDict_Check(dict)) {
|
|
||||||
Py_DECREF(dict);
|
|
||||||
dict = PyDict_New();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Copy __dict__ to avoid mutating it. */
|
|
||||||
PyObject *temp = PyDict_Copy(dict);
|
|
||||||
Py_DECREF(dict);
|
|
||||||
dict = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dict == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* 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 {
|
|
||||||
if (merge_class_dict(dict, itsclass) != 0)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 *
|
static PyObject *
|
||||||
_dir_object(PyObject *obj)
|
_dir_object(PyObject *obj)
|
||||||
{
|
{
|
||||||
PyObject *result = NULL;
|
PyObject *result;
|
||||||
static PyObject *dir_str = NULL;
|
static PyObject *dir_str = NULL;
|
||||||
PyObject *dirfunc = _PyObject_LookupSpecial(obj, "__dir__", &dir_str);
|
PyObject *dirfunc = _PyObject_LookupSpecial(obj, "__dir__", &dir_str);
|
||||||
|
|
||||||
assert(obj);
|
assert(obj);
|
||||||
if (dirfunc == NULL) {
|
if (dirfunc == NULL) {
|
||||||
if (PyErr_Occurred())
|
if (!PyErr_Occurred())
|
||||||
|
PyErr_SetString(PyExc_TypeError, "object does not provide __dir__");
|
||||||
return NULL;
|
return NULL;
|
||||||
/* use default implementation */
|
|
||||||
if (PyModule_Check(obj))
|
|
||||||
result = _specialized_dir_module(obj);
|
|
||||||
else if (PyType_Check(obj))
|
|
||||||
result = _specialized_dir_type(obj);
|
|
||||||
else
|
|
||||||
result = _generic_dir(obj);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
/* use __dir__ */
|
/* use __dir__ */
|
||||||
result = PyObject_CallFunctionObjArgs(dirfunc, NULL);
|
result = PyObject_CallFunctionObjArgs(dirfunc, NULL);
|
||||||
Py_DECREF(dirfunc);
|
Py_DECREF(dirfunc);
|
||||||
|
@ -1396,7 +1238,6 @@ _dir_object(PyObject *obj)
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
result = NULL;
|
result = NULL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2572,6 +2572,82 @@ type_prepare(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
return PyDict_New();
|
return PyDict_New();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
defined, as it's expected that only the final set of dict keys is
|
||||||
|
interesting.
|
||||||
|
Return 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
merge_class_dict(PyObject *dict, PyObject *aclass)
|
||||||
|
{
|
||||||
|
PyObject *classdict;
|
||||||
|
PyObject *bases;
|
||||||
|
|
||||||
|
assert(PyDict_Check(dict));
|
||||||
|
assert(aclass);
|
||||||
|
|
||||||
|
/* Merge in the type's dict (if any). */
|
||||||
|
classdict = PyObject_GetAttrString(aclass, "__dict__");
|
||||||
|
if (classdict == NULL)
|
||||||
|
PyErr_Clear();
|
||||||
|
else {
|
||||||
|
int status = PyDict_Update(dict, classdict);
|
||||||
|
Py_DECREF(classdict);
|
||||||
|
if (status < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recursively merge in the base types' (if any) dicts. */
|
||||||
|
bases = PyObject_GetAttrString(aclass, "__bases__");
|
||||||
|
if (bases == NULL)
|
||||||
|
PyErr_Clear();
|
||||||
|
else {
|
||||||
|
/* We have no guarantee that bases is a real tuple */
|
||||||
|
Py_ssize_t i, n;
|
||||||
|
n = PySequence_Size(bases); /* This better be right */
|
||||||
|
if (n < 0)
|
||||||
|
PyErr_Clear();
|
||||||
|
else {
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
int status;
|
||||||
|
PyObject *base = PySequence_GetItem(bases, i);
|
||||||
|
if (base == NULL) {
|
||||||
|
Py_DECREF(bases);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
status = merge_class_dict(dict, base);
|
||||||
|
Py_DECREF(base);
|
||||||
|
if (status < 0) {
|
||||||
|
Py_DECREF(bases);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_DECREF(bases);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* __dir__ for 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 *
|
||||||
|
type_dir(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *result = NULL;
|
||||||
|
PyObject *dict = PyDict_New();
|
||||||
|
|
||||||
|
if (dict != NULL && merge_class_dict(dict, self) == 0)
|
||||||
|
result = PyDict_Keys(dict);
|
||||||
|
|
||||||
|
Py_XDECREF(dict);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef type_methods[] = {
|
static PyMethodDef type_methods[] = {
|
||||||
{"mro", (PyCFunction)mro_external, METH_NOARGS,
|
{"mro", (PyCFunction)mro_external, METH_NOARGS,
|
||||||
PyDoc_STR("mro() -> list\nreturn a type's method resolution order")},
|
PyDoc_STR("mro() -> list\nreturn a type's method resolution order")},
|
||||||
|
@ -2585,6 +2661,8 @@ static PyMethodDef type_methods[] = {
|
||||||
PyDoc_STR("__instancecheck__() -> check if an object is an instance")},
|
PyDoc_STR("__instancecheck__() -> check if an object is an instance")},
|
||||||
{"__subclasscheck__", type___subclasscheck__, METH_O,
|
{"__subclasscheck__", type___subclasscheck__, METH_O,
|
||||||
PyDoc_STR("__subclasscheck__() -> check if a class is a subclass")},
|
PyDoc_STR("__subclasscheck__() -> check if a class is a subclass")},
|
||||||
|
{"__dir__", type_dir, METH_NOARGS,
|
||||||
|
PyDoc_STR("__dir__() -> specialized __dir__ implementation for types")},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3438,6 +3516,53 @@ object_sizeof(PyObject *self, PyObject *args)
|
||||||
return PyLong_FromSsize_t(res);
|
return PyLong_FromSsize_t(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* __dir__ for generic objects: returns __dict__, __class__,
|
||||||
|
and recursively up the __class__.__bases__ chain.
|
||||||
|
*/
|
||||||
|
static PyObject *
|
||||||
|
object_dir(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *result = NULL;
|
||||||
|
PyObject *dict = NULL;
|
||||||
|
PyObject *itsclass = NULL;
|
||||||
|
|
||||||
|
/* Get __dict__ (which may or may not be a real dict...) */
|
||||||
|
dict = PyObject_GetAttrString(self, "__dict__");
|
||||||
|
if (dict == NULL) {
|
||||||
|
PyErr_Clear();
|
||||||
|
dict = PyDict_New();
|
||||||
|
}
|
||||||
|
else if (!PyDict_Check(dict)) {
|
||||||
|
Py_DECREF(dict);
|
||||||
|
dict = PyDict_New();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Copy __dict__ to avoid mutating it. */
|
||||||
|
PyObject *temp = PyDict_Copy(dict);
|
||||||
|
Py_DECREF(dict);
|
||||||
|
dict = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Merge in attrs reachable from its class. */
|
||||||
|
itsclass = PyObject_GetAttrString(self, "__class__");
|
||||||
|
if (itsclass == NULL)
|
||||||
|
/* XXX(tomer): Perhaps fall back to obj->ob_type if no
|
||||||
|
__class__ exists? */
|
||||||
|
PyErr_Clear();
|
||||||
|
else if (merge_class_dict(dict, itsclass) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
result = PyDict_Keys(dict);
|
||||||
|
/* fall through */
|
||||||
|
error:
|
||||||
|
Py_XDECREF(itsclass);
|
||||||
|
Py_XDECREF(dict);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef object_methods[] = {
|
static PyMethodDef object_methods[] = {
|
||||||
{"__reduce_ex__", object_reduce_ex, METH_VARARGS,
|
{"__reduce_ex__", object_reduce_ex, METH_VARARGS,
|
||||||
PyDoc_STR("helper for pickle")},
|
PyDoc_STR("helper for pickle")},
|
||||||
|
@ -3449,6 +3574,8 @@ static PyMethodDef object_methods[] = {
|
||||||
PyDoc_STR("default object formatter")},
|
PyDoc_STR("default object formatter")},
|
||||||
{"__sizeof__", object_sizeof, METH_NOARGS,
|
{"__sizeof__", object_sizeof, METH_NOARGS,
|
||||||
PyDoc_STR("__sizeof__() -> size of object in memory, in bytes")},
|
PyDoc_STR("__sizeof__() -> size of object in memory, in bytes")},
|
||||||
|
{"__dir__", object_dir, METH_NOARGS,
|
||||||
|
PyDoc_STR("__dir__() -> default dir() implementation")},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue