summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_descrtut.py1
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/moduleobject.c30
-rw-r--r--Objects/object.c195
-rw-r--r--Objects/typeobject.c127
5 files changed, 178 insertions, 178 deletions
diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py
index c3355b9..f495e18 100644
--- a/Lib/test/test_descrtut.py
+++ b/Lib/test/test_descrtut.py
@@ -170,6 +170,7 @@ You can get the information from the list type:
'__contains__',
'__delattr__',
'__delitem__',
+ '__dir__',
'__doc__',
'__eq__',
'__format__',
diff --git a/Misc/NEWS b/Misc/NEWS
index 967e2ef..47b9c32 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
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
besides AttributeError found on lookup to be propagated.
diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c
index 06f58d8..d922249 100644
--- a/Objects/moduleobject.c
+++ b/Objects/moduleobject.c
@@ -413,6 +413,34 @@ module_clear(PyModuleObject *m)
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,
"module(name[, doc])\n\
@@ -449,7 +477,7 @@ PyTypeObject PyModule_Type = {
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
- 0, /* tp_methods */
+ module_methods, /* tp_methods */
module_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
diff --git a/Objects/object.c b/Objects/object.c
index d8e2ffb..e42c1d9 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1182,66 +1182,6 @@ PyCallable_Check(PyObject *x)
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. */
static PyObject *
@@ -1269,133 +1209,34 @@ _dir_locals(void)
return names;
}
-/* 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();
-
- 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. */
+/* Helper for PyObject_Dir: object introspection. */
static PyObject *
_dir_object(PyObject *obj)
{
- PyObject *result = NULL;
+ PyObject *result;
static PyObject *dir_str = NULL;
PyObject *dirfunc = _PyObject_LookupSpecial(obj, "__dir__", &dir_str);
assert(obj);
if (dirfunc == NULL) {
- if (PyErr_Occurred())
- 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);
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_TypeError, "object does not provide __dir__");
+ return NULL;
}
- else {
- /* use __dir__ */
- result = PyObject_CallFunctionObjArgs(dirfunc, NULL);
- Py_DECREF(dirfunc);
- if (result == NULL)
- return NULL;
+ /* use __dir__ */
+ result = PyObject_CallFunctionObjArgs(dirfunc, NULL);
+ Py_DECREF(dirfunc);
+ if (result == NULL)
+ return NULL;
- /* result must be a list */
- /* XXX(gbrandl): could also check if all items are strings */
- if (!PyList_Check(result)) {
- PyErr_Format(PyExc_TypeError,
- "__dir__() must return a list, not %.200s",
- Py_TYPE(result)->tp_name);
- Py_DECREF(result);
- result = NULL;
- }
+ /* result must be a list */
+ /* XXX(gbrandl): could also check if all items are strings */
+ if (!PyList_Check(result)) {
+ PyErr_Format(PyExc_TypeError,
+ "__dir__() must return a list, not %.200s",
+ Py_TYPE(result)->tp_name);
+ Py_DECREF(result);
+ result = NULL;
}
return result;
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 434608f..128b9fe 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -2572,6 +2572,82 @@ type_prepare(PyObject *self, PyObject *args, PyObject *kwds)
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[] = {
{"mro", (PyCFunction)mro_external, METH_NOARGS,
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")},
{"__subclasscheck__", type___subclasscheck__, METH_O,
PyDoc_STR("__subclasscheck__() -> check if a class is a subclass")},
+ {"__dir__", type_dir, METH_NOARGS,
+ PyDoc_STR("__dir__() -> specialized __dir__ implementation for types")},
{0}
};
@@ -3438,6 +3516,53 @@ object_sizeof(PyObject *self, PyObject *args)
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[] = {
{"__reduce_ex__", object_reduce_ex, METH_VARARGS,
PyDoc_STR("helper for pickle")},
@@ -3449,6 +3574,8 @@ static PyMethodDef object_methods[] = {
PyDoc_STR("default object formatter")},
{"__sizeof__", object_sizeof, METH_NOARGS,
PyDoc_STR("__sizeof__() -> size of object in memory, in bytes")},
+ {"__dir__", object_dir, METH_NOARGS,
+ PyDoc_STR("__dir__() -> default dir() implementation")},
{0}
};