From 3bbb72265411585e64a5d2ccb5ba51763f20e311 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 11 Jun 2011 16:12:08 -0500 Subject: allow __dir__ to return any sequence --- Doc/reference/datamodel.rst | 3 ++- Lib/test/test_builtin.py | 10 +++++++++- Misc/NEWS | 3 +++ Objects/object.c | 45 +++++++++++++++------------------------------ 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 129f987..e628a02 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1343,7 +1343,8 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. .. method:: object.__dir__(self) - Called when :func:`dir` is called on the object. A list must be returned. + Called when :func:`dir` is called on the object. A sequence must be + returned. :func:`dir` converts the returned sequence to a list and sorts it. .. _descriptors: diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 97619cf..ce1586f 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -372,7 +372,15 @@ class BuiltinTest(unittest.TestCase): f = Foo() self.assertTrue(dir(f) == ["ga", "kan", "roo"]) - # dir(obj__dir__not_list) + # dir(obj__dir__tuple) + class Foo(object): + def __dir__(self): + return ("b", "c", "a") + res = dir(Foo()) + self.assertIsInstance(res, list) + self.assertTrue(res == ["a", "b", "c"]) + + # dir(obj__dir__not_sequence) class Foo(object): def __dir__(self): return 7 diff --git a/Misc/NEWS b/Misc/NEWS index e2538fc..e13f72c 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1? Core and Builtins ----------------- +- Loosen type restrictions on the __dir__ method. __dir__ can now return any + sequence, which will be converted to a list and sorted by dir(). + - Issue #12265: Make error messages produced by passing an invalid set of arguments to a function more informative. diff --git a/Objects/object.c b/Objects/object.c index e42c1d9..80ffddb 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1205,6 +1205,10 @@ _dir_locals(void) Py_DECREF(names); return NULL; } + if (PyList_Sort(names)) { + Py_DECREF(names); + return NULL; + } /* the locals don't need to be DECREF'd */ return names; } @@ -1213,7 +1217,7 @@ _dir_locals(void) static PyObject * _dir_object(PyObject *obj) { - PyObject *result; + PyObject *result, *sorted; static PyObject *dir_str = NULL; PyObject *dirfunc = _PyObject_LookupSpecial(obj, "__dir__", &dir_str); @@ -1228,18 +1232,16 @@ _dir_object(PyObject *obj) 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; + /* return sorted(result) */ + sorted = PySequence_List(result); + Py_DECREF(result); + if (sorted == NULL) + return NULL; + if (PyList_Sort(sorted)) { + Py_DECREF(sorted); + return NULL; } - - return result; + return sorted; } /* Implementation of dir() -- if obj is NULL, returns the names in the current @@ -1249,24 +1251,7 @@ _dir_object(PyObject *obj) 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; + return (obj == NULL) ? _dir_locals() : _dir_object(obj); } /* -- cgit v0.12