mirror of
https://github.com/python/cpython.git
synced 2025-12-09 10:37:17 +00:00
Make dir() wordier (see the new docstring). The new behavior is a mixed
bag. It's clearly wrong for classic classes, at heart because a classic class doesn't have a __class__ attribute, and I'm unclear on whether that's feature or bug. I'll repair this once I find out (in the meantime, dir() applied to classic classes won't find the base classes, while dir() applied to a classic-class instance *will* find the base classes but not *their* base classes). Please give the new dir() a try and see whether you love it or hate it. The new dir([]) behavior is something I could come to love. Here's something to hate: >>> class C: ... pass ... >>> c = C() >>> dir(c) ['__doc__', '__module__'] >>> The idea that an instance has a __doc__ attribute is jarring (of course it's really c.__class__.__doc__ == C.__doc__; likewise for __module__). OTOH, the code already has too many special cases, and dir(x) doesn't have a compelling or clear purpose when x isn't a module.
This commit is contained in:
parent
95c99e57b3
commit
5d2b77cf31
5 changed files with 207 additions and 65 deletions
|
|
@ -172,6 +172,54 @@ def dict_constructor():
|
||||||
d = dictionary(mapping=Mapping())
|
d = dictionary(mapping=Mapping())
|
||||||
verify(d == Mapping.dict)
|
verify(d == Mapping.dict)
|
||||||
|
|
||||||
|
def test_dir():
|
||||||
|
if verbose:
|
||||||
|
print "Testing dir() ..."
|
||||||
|
junk = 12
|
||||||
|
verify(dir() == ['junk'])
|
||||||
|
del junk
|
||||||
|
|
||||||
|
# Just make sure these don't blow up!
|
||||||
|
for arg in 2, 2L, 2j, 2e0, [2], "2", u"2", (2,), {2:2}, type, test_dir:
|
||||||
|
dir(arg)
|
||||||
|
|
||||||
|
# Check some details here because classic classes aren't working
|
||||||
|
# reasonably, and I want this to fail (eventually).
|
||||||
|
class C:
|
||||||
|
Cdata = 1
|
||||||
|
def Cmethod(self): pass
|
||||||
|
|
||||||
|
cstuff = ['Cdata', 'Cmethod', '__doc__', '__module__']
|
||||||
|
verify(dir(C) == cstuff)
|
||||||
|
|
||||||
|
c = C() # c.__doc__ is an odd thing to see here; ditto c.__module__.
|
||||||
|
verify(dir(c) == cstuff)
|
||||||
|
|
||||||
|
c.cdata = 2
|
||||||
|
c.cmethod = lambda self: 0
|
||||||
|
verify(dir(c) == cstuff + ['cdata', 'cmethod'])
|
||||||
|
|
||||||
|
class A(C):
|
||||||
|
Adata = 1
|
||||||
|
def Amethod(self): pass
|
||||||
|
astuff = ['Adata', 'Amethod', '__doc__', '__module__']
|
||||||
|
# This isn't finding C's stuff at all.
|
||||||
|
verify(dir(A) == astuff)
|
||||||
|
# But this is! It's because a.__class__ exists but A.__class__ doesn't.
|
||||||
|
a = A()
|
||||||
|
verify(dir(a) == astuff[:2] + cstuff)
|
||||||
|
|
||||||
|
# The story for new-style classes is quite different.
|
||||||
|
class C(object):
|
||||||
|
Cdata = 1
|
||||||
|
def Cmethod(self): pass
|
||||||
|
class A(C):
|
||||||
|
Adata = 1
|
||||||
|
def Amethod(self): pass
|
||||||
|
d = dir(A)
|
||||||
|
for expected in 'Cdata', 'Cmethod', 'Adata', 'Amethod':
|
||||||
|
verify(expected in d)
|
||||||
|
|
||||||
binops = {
|
binops = {
|
||||||
'add': '+',
|
'add': '+',
|
||||||
'sub': '-',
|
'sub': '-',
|
||||||
|
|
@ -1349,6 +1397,7 @@ def all():
|
||||||
lists()
|
lists()
|
||||||
dicts()
|
dicts()
|
||||||
dict_constructor()
|
dict_constructor()
|
||||||
|
test_dir()
|
||||||
ints()
|
ints()
|
||||||
longs()
|
longs()
|
||||||
floats()
|
floats()
|
||||||
|
|
|
||||||
|
|
@ -97,14 +97,15 @@ just like classic classes:
|
||||||
>>> a.default = -1000
|
>>> a.default = -1000
|
||||||
>>> print a["noway"]
|
>>> print a["noway"]
|
||||||
-1000
|
-1000
|
||||||
>>> print dir(a)
|
>>> 'default' in dir(a)
|
||||||
['default']
|
1
|
||||||
>>> a.x1 = 100
|
>>> a.x1 = 100
|
||||||
>>> a.x2 = 200
|
>>> a.x2 = 200
|
||||||
>>> print a.x1
|
>>> print a.x1
|
||||||
100
|
100
|
||||||
>>> print dir(a)
|
>>> d = dir(a)
|
||||||
['default', 'x1', 'x2']
|
>>> 'default' in d and 'x1' in d and 'x2' in d
|
||||||
|
1
|
||||||
>>> print a.__dict__
|
>>> print a.__dict__
|
||||||
{'default': -1000, 'x2': 200, 'x1': 100}
|
{'default': -1000, 'x2': 200, 'x1': 100}
|
||||||
>>>
|
>>>
|
||||||
|
|
|
||||||
|
|
@ -383,14 +383,8 @@ From the Iterators list, about the types of these things.
|
||||||
>>> i = g()
|
>>> i = g()
|
||||||
>>> type(i)
|
>>> type(i)
|
||||||
<type 'generator'>
|
<type 'generator'>
|
||||||
|
>>> [s for s in dir(i) if not s.startswith('_')]
|
||||||
XXX dir(object) *generally* doesn't return useful stuff in descr-branch.
|
|
||||||
>>> dir(i)
|
|
||||||
[]
|
|
||||||
|
|
||||||
Was hoping to see this instead:
|
|
||||||
['gi_frame', 'gi_running', 'next']
|
['gi_frame', 'gi_running', 'next']
|
||||||
|
|
||||||
>>> print i.next.__doc__
|
>>> print i.next.__doc__
|
||||||
x.next() -> the next value, or raise StopIteration
|
x.next() -> the next value, or raise StopIteration
|
||||||
>>> iter(i) is i
|
>>> iter(i) is i
|
||||||
|
|
|
||||||
17
Misc/NEWS
17
Misc/NEWS
|
|
@ -3,6 +3,23 @@ What's New in Python 2.2a3?
|
||||||
|
|
||||||
Core
|
Core
|
||||||
|
|
||||||
|
- The builtin dir() now returns more information, and sometimes much
|
||||||
|
more, generally naming all attributes of an object, and all attributes
|
||||||
|
reachable from the object via its class, and from its class's base
|
||||||
|
classes, and so on from them too. Example: in 2.2a2, dir([]) returned
|
||||||
|
an empty list. In 2.2a3,
|
||||||
|
|
||||||
|
>>> dir([])
|
||||||
|
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
|
||||||
|
'__eq__', '__ge__', '__getattr__', '__getitem__', '__getslice__',
|
||||||
|
'__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__le__',
|
||||||
|
'__len__', '__lt__', '__mul__', '__ne__', '__new__', '__repr__',
|
||||||
|
'__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__',
|
||||||
|
'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
|
||||||
|
'reverse', 'sort']
|
||||||
|
|
||||||
|
dir(module) continues to return only the module's attributes, though.
|
||||||
|
|
||||||
- Overflowing operations on plain ints now return a long int rather
|
- Overflowing operations on plain ints now return a long int rather
|
||||||
than raising OverflowError. This is a partial implementation of PEP
|
than raising OverflowError. This is a partial implementation of PEP
|
||||||
237. You can use -Wdefault::OverflowWarning to enable a warning for
|
237. You can use -Wdefault::OverflowWarning to enable a warning for
|
||||||
|
|
|
||||||
|
|
@ -426,80 +426,161 @@ the effects of any future statements in effect in the code calling\n\
|
||||||
compile; if absent or zero these statements do influence the compilation,\n\
|
compile; if absent or zero these statements do influence the compilation,\n\
|
||||||
in addition to any features explicitly specified.";
|
in addition to any features explicitly specified.";
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
/* XXX Class objects fail the PyType_Check check. Don't
|
||||||
|
XXX know of others. */
|
||||||
|
/* assert(PyType_Check(aclass)); */
|
||||||
|
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) {
|
||||||
|
int i, n;
|
||||||
|
assert(PyTuple_Check(bases));
|
||||||
|
n = PyTuple_GET_SIZE(bases);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||||
|
if (merge_class_dict(dict, base) < 0) {
|
||||||
|
Py_DECREF(bases);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_DECREF(bases);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
builtin_dir(PyObject *self, PyObject *args)
|
builtin_dir(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
static char *attrlist[] = {"__members__", "__methods__", NULL};
|
PyObject *arg = NULL;
|
||||||
PyObject *v = NULL, *l = NULL, *m = NULL;
|
/* Set exactly one of these non-NULL before the end. */
|
||||||
PyObject *d, *x;
|
PyObject *result = NULL; /* result list */
|
||||||
int i;
|
PyObject *masterdict = NULL; /* result is masterdict.keys() */
|
||||||
char **s;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|O:dir", &v))
|
if (!PyArg_ParseTuple(args, "|O:dir", &arg))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (v == NULL) {
|
|
||||||
x = PyEval_GetLocals();
|
/* If no arg, return the locals. */
|
||||||
if (x == NULL)
|
if (arg == NULL) {
|
||||||
|
PyObject *locals = PyEval_GetLocals();
|
||||||
|
if (locals == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
l = PyMapping_Keys(x);
|
result = PyMapping_Keys(locals);
|
||||||
if (l == NULL)
|
if (result == NULL)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Elif this is some form of module, we only want its dict. */
|
||||||
|
else if (PyObject_TypeCheck(arg, &PyModule_Type)) {
|
||||||
|
masterdict = PyObject_GetAttrString(arg, "__dict__");
|
||||||
|
if (masterdict == NULL)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Elif some form of type, recurse. */
|
||||||
|
else if (PyType_Check(arg)) {
|
||||||
|
masterdict = PyDict_New();
|
||||||
|
if (masterdict == NULL)
|
||||||
|
goto error;
|
||||||
|
if (merge_class_dict(masterdict, arg) < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else look at its dict, and the attrs reachable from its class. */
|
||||||
|
else {
|
||||||
|
PyObject *itsclass;
|
||||||
|
/* Create a dict to start with. */
|
||||||
|
masterdict = PyObject_GetAttrString(arg, "__dict__");
|
||||||
|
if (masterdict == NULL) {
|
||||||
|
PyErr_Clear();
|
||||||
|
masterdict = PyDict_New();
|
||||||
|
if (masterdict == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
d = PyObject_GetAttrString(v, "__dict__");
|
/* The object may have returned a reference to its
|
||||||
if (d == NULL)
|
dict, so copy it to avoid mutating it. */
|
||||||
|
PyObject *temp = PyDict_Copy(masterdict);
|
||||||
|
if (temp == NULL)
|
||||||
|
goto error;
|
||||||
|
Py_DECREF(masterdict);
|
||||||
|
masterdict = temp;
|
||||||
|
}
|
||||||
|
/* Merge in attrs reachable from its class. */
|
||||||
|
itsclass = PyObject_GetAttrString(arg, "__class__");
|
||||||
|
/* XXX Sometimes this is null! Like after "class C: pass",
|
||||||
|
C.__class__ raises AttributeError. Don't know of other
|
||||||
|
cases. */
|
||||||
|
if (itsclass == NULL)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
else {
|
else {
|
||||||
l = PyMapping_Keys(d);
|
int status = merge_class_dict(masterdict, itsclass);
|
||||||
if (l == NULL)
|
Py_DECREF(itsclass);
|
||||||
PyErr_Clear();
|
if (status < 0)
|
||||||
Py_DECREF(d);
|
|
||||||
}
|
|
||||||
if (l == NULL) {
|
|
||||||
l = PyList_New(0);
|
|
||||||
if (l == NULL)
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
for (s = attrlist; *s != NULL; s++) {
|
|
||||||
m = PyObject_GetAttrString(v, *s);
|
|
||||||
if (m == NULL) {
|
|
||||||
PyErr_Clear();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
for (i = 0; ; i++) {
|
|
||||||
x = PySequence_GetItem(m, i);
|
assert((result == NULL) ^ (masterdict == NULL));
|
||||||
if (x == NULL) {
|
if (masterdict != NULL) {
|
||||||
PyErr_Clear();
|
/* The result comes from its keys. */
|
||||||
break;
|
assert(result == NULL);
|
||||||
}
|
result = PyMapping_Keys(masterdict);
|
||||||
if (PyList_Append(l, x) != 0) {
|
if (result == NULL)
|
||||||
Py_DECREF(x);
|
|
||||||
Py_DECREF(m);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
Py_DECREF(x);
|
|
||||||
}
|
assert(result);
|
||||||
Py_DECREF(m);
|
if (PyList_Sort(result) != 0)
|
||||||
}
|
|
||||||
}
|
|
||||||
if (PyList_Sort(l) != 0)
|
|
||||||
goto error;
|
goto error;
|
||||||
return l;
|
else
|
||||||
|
goto normal_return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
Py_XDECREF(l);
|
Py_XDECREF(result);
|
||||||
return NULL;
|
result = NULL;
|
||||||
|
/* fall through */
|
||||||
|
normal_return:
|
||||||
|
Py_XDECREF(masterdict);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char dir_doc[] =
|
static char dir_doc[] =
|
||||||
"dir([object]) -> list of strings\n\
|
"dir([object]) -> list of strings\n"
|
||||||
\n\
|
"\n"
|
||||||
Return an alphabetized list of names comprising (some of) the attributes\n\
|
"Return an alphabetized list of names comprising (some of) the attributes\n"
|
||||||
of the given object. Without an argument, the names in the current scope\n\
|
"of the given object, and of attributes reachable from it:\n"
|
||||||
are listed. With an instance argument, only the instance attributes are\n\
|
"\n"
|
||||||
returned. With a class argument, attributes of the base class are not\n\
|
"No argument: the names in the current scope.\n"
|
||||||
returned. For other types or arguments, this may list members or methods.";
|
"Module object: the module attributes.\n"
|
||||||
|
"Type object: its attributes, and recursively the attributes of its bases.\n"
|
||||||
|
"Otherwise: its attributes, its class's attributes, and recursively the\n"
|
||||||
|
" attributes of its class's base classes.";
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
builtin_divmod(PyObject *self, PyObject *args)
|
builtin_divmod(PyObject *self, PyObject *args)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue