Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5d2b77c

Browse files
committed
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.
1 parent 95c99e5 commit 5d2b77c

5 files changed

Lines changed: 206 additions & 64 deletions

File tree

Lib/test/test_descr.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,54 @@ def __getitem__(self, i):
172172
d = dictionary(mapping=Mapping())
173173
verify(d == Mapping.dict)
174174

175+
def test_dir():
176+
if verbose:
177+
print "Testing dir() ..."
178+
junk = 12
179+
verify(dir() == ['junk'])
180+
del junk
181+
182+
# Just make sure these don't blow up!
183+
for arg in 2, 2L, 2j, 2e0, [2], "2", u"2", (2,), {2:2}, type, test_dir:
184+
dir(arg)
185+
186+
# Check some details here because classic classes aren't working
187+
# reasonably, and I want this to fail (eventually).
188+
class C:
189+
Cdata = 1
190+
def Cmethod(self): pass
191+
192+
cstuff = ['Cdata', 'Cmethod', '__doc__', '__module__']
193+
verify(dir(C) == cstuff)
194+
195+
c = C() # c.__doc__ is an odd thing to see here; ditto c.__module__.
196+
verify(dir(c) == cstuff)
197+
198+
c.cdata = 2
199+
c.cmethod = lambda self: 0
200+
verify(dir(c) == cstuff + ['cdata', 'cmethod'])
201+
202+
class A(C):
203+
Adata = 1
204+
def Amethod(self): pass
205+
astuff = ['Adata', 'Amethod', '__doc__', '__module__']
206+
# This isn't finding C's stuff at all.
207+
verify(dir(A) == astuff)
208+
# But this is! It's because a.__class__ exists but A.__class__ doesn't.
209+
a = A()
210+
verify(dir(a) == astuff[:2] + cstuff)
211+
212+
# The story for new-style classes is quite different.
213+
class C(object):
214+
Cdata = 1
215+
def Cmethod(self): pass
216+
class A(C):
217+
Adata = 1
218+
def Amethod(self): pass
219+
d = dir(A)
220+
for expected in 'Cdata', 'Cmethod', 'Adata', 'Amethod':
221+
verify(expected in d)
222+
175223
binops = {
176224
'add': '+',
177225
'sub': '-',
@@ -1349,6 +1397,7 @@ def all():
13491397
lists()
13501398
dicts()
13511399
dict_constructor()
1400+
test_dir()
13521401
ints()
13531402
longs()
13541403
floats()

Lib/test/test_descrtut.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,15 @@ def merge(self, other):
9797
>>> a.default = -1000
9898
>>> print a["noway"]
9999
-1000
100-
>>> print dir(a)
101-
['default']
100+
>>> 'default' in dir(a)
101+
1
102102
>>> a.x1 = 100
103103
>>> a.x2 = 200
104104
>>> print a.x1
105105
100
106-
>>> print dir(a)
107-
['default', 'x1', 'x2']
106+
>>> d = dir(a)
107+
>>> 'default' in d and 'x1' in d and 'x2' in d
108+
1
108109
>>> print a.__dict__
109110
{'default': -1000, 'x2': 200, 'x1': 100}
110111
>>>

Lib/test/test_generators.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -383,14 +383,8 @@
383383
>>> i = g()
384384
>>> type(i)
385385
<type 'generator'>
386-
387-
XXX dir(object) *generally* doesn't return useful stuff in descr-branch.
388-
>>> dir(i)
389-
[]
390-
391-
Was hoping to see this instead:
386+
>>> [s for s in dir(i) if not s.startswith('_')]
392387
['gi_frame', 'gi_running', 'next']
393-
394388
>>> print i.next.__doc__
395389
x.next() -> the next value, or raise StopIteration
396390
>>> iter(i) is i

Misc/NEWS

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,23 @@ What's New in Python 2.2a3?
33

44
Core
55

6+
- The builtin dir() now returns more information, and sometimes much
7+
more, generally naming all attributes of an object, and all attributes
8+
reachable from the object via its class, and from its class's base
9+
classes, and so on from them too. Example: in 2.2a2, dir([]) returned
10+
an empty list. In 2.2a3,
11+
12+
>>> dir([])
13+
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
14+
'__eq__', '__ge__', '__getattr__', '__getitem__', '__getslice__',
15+
'__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__le__',
16+
'__len__', '__lt__', '__mul__', '__ne__', '__new__', '__repr__',
17+
'__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__',
18+
'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
19+
'reverse', 'sort']
20+
21+
dir(module) continues to return only the module's attributes, though.
22+
623
- Overflowing operations on plain ints now return a long int rather
724
than raising OverflowError. This is a partial implementation of PEP
825
237. You can use -Wdefault::OverflowWarning to enable a warning for

Python/bltinmodule.c

Lines changed: 134 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -426,80 +426,161 @@ the effects of any future statements in effect in the code calling\n\
426426
compile; if absent or zero these statements do influence the compilation,\n\
427427
in addition to any features explicitly specified.";
428428

429+
/* Merge the __dict__ of aclass into dict, and recursively also all
430+
the __dict__s of aclass's base classes. The order of merging isn't
431+
defined, as it's expected that only the final set of dict keys is
432+
interesting.
433+
Return 0 on success, -1 on error.
434+
*/
435+
436+
static int
437+
merge_class_dict(PyObject* dict, PyObject* aclass)
438+
{
439+
PyObject *classdict;
440+
PyObject *bases;
441+
442+
assert(PyDict_Check(dict));
443+
/* XXX Class objects fail the PyType_Check check. Don't
444+
XXX know of others. */
445+
/* assert(PyType_Check(aclass)); */
446+
assert(aclass);
447+
448+
/* Merge in the type's dict (if any). */
449+
classdict = PyObject_GetAttrString(aclass, "__dict__");
450+
if (classdict == NULL)
451+
PyErr_Clear();
452+
else {
453+
int status = PyDict_Update(dict, classdict);
454+
Py_DECREF(classdict);
455+
if (status < 0)
456+
return -1;
457+
}
458+
459+
/* Recursively merge in the base types' (if any) dicts. */
460+
bases = PyObject_GetAttrString(aclass, "__bases__");
461+
if (bases != NULL) {
462+
int i, n;
463+
assert(PyTuple_Check(bases));
464+
n = PyTuple_GET_SIZE(bases);
465+
for (i = 0; i < n; i++) {
466+
PyObject *base = PyTuple_GET_ITEM(bases, i);
467+
if (merge_class_dict(dict, base) < 0) {
468+
Py_DECREF(bases);
469+
return -1;
470+
}
471+
}
472+
Py_DECREF(bases);
473+
}
474+
return 0;
475+
}
429476

430477
static PyObject *
431478
builtin_dir(PyObject *self, PyObject *args)
432479
{
433-
static char *attrlist[] = {"__members__", "__methods__", NULL};
434-
PyObject *v = NULL, *l = NULL, *m = NULL;
435-
PyObject *d, *x;
436-
int i;
437-
char **s;
480+
PyObject *arg = NULL;
481+
/* Set exactly one of these non-NULL before the end. */
482+
PyObject *result = NULL; /* result list */
483+
PyObject *masterdict = NULL; /* result is masterdict.keys() */
438484

439-
if (!PyArg_ParseTuple(args, "|O:dir", &v))
485+
if (!PyArg_ParseTuple(args, "|O:dir", &arg))
440486
return NULL;
441-
if (v == NULL) {
442-
x = PyEval_GetLocals();
443-
if (x == NULL)
487+
488+
/* If no arg, return the locals. */
489+
if (arg == NULL) {
490+
PyObject *locals = PyEval_GetLocals();
491+
if (locals == NULL)
444492
goto error;
445-
l = PyMapping_Keys(x);
446-
if (l == NULL)
493+
result = PyMapping_Keys(locals);
494+
if (result == NULL)
495+
goto error;
496+
}
497+
498+
/* Elif this is some form of module, we only want its dict. */
499+
else if (PyObject_TypeCheck(arg, &PyModule_Type)) {
500+
masterdict = PyObject_GetAttrString(arg, "__dict__");
501+
if (masterdict == NULL)
502+
goto error;
503+
}
504+
505+
/* Elif some form of type, recurse. */
506+
else if (PyType_Check(arg)) {
507+
masterdict = PyDict_New();
508+
if (masterdict == NULL)
509+
goto error;
510+
if (merge_class_dict(masterdict, arg) < 0)
447511
goto error;
448512
}
513+
514+
/* Else look at its dict, and the attrs reachable from its class. */
449515
else {
450-
d = PyObject_GetAttrString(v, "__dict__");
451-
if (d == NULL)
516+
PyObject *itsclass;
517+
/* Create a dict to start with. */
518+
masterdict = PyObject_GetAttrString(arg, "__dict__");
519+
if (masterdict == NULL) {
452520
PyErr_Clear();
453-
else {
454-
l = PyMapping_Keys(d);
455-
if (l == NULL)
456-
PyErr_Clear();
457-
Py_DECREF(d);
521+
masterdict = PyDict_New();
522+
if (masterdict == NULL)
523+
goto error;
458524
}
459-
if (l == NULL) {
460-
l = PyList_New(0);
461-
if (l == NULL)
525+
else {
526+
/* The object may have returned a reference to its
527+
dict, so copy it to avoid mutating it. */
528+
PyObject *temp = PyDict_Copy(masterdict);
529+
if (temp == NULL)
462530
goto error;
531+
Py_DECREF(masterdict);
532+
masterdict = temp;
463533
}
464-
for (s = attrlist; *s != NULL; s++) {
465-
m = PyObject_GetAttrString(v, *s);
466-
if (m == NULL) {
467-
PyErr_Clear();
468-
continue;
469-
}
470-
for (i = 0; ; i++) {
471-
x = PySequence_GetItem(m, i);
472-
if (x == NULL) {
473-
PyErr_Clear();
474-
break;
475-
}
476-
if (PyList_Append(l, x) != 0) {
477-
Py_DECREF(x);
478-
Py_DECREF(m);
479-
goto error;
480-
}
481-
Py_DECREF(x);
482-
}
483-
Py_DECREF(m);
534+
/* Merge in attrs reachable from its class. */
535+
itsclass = PyObject_GetAttrString(arg, "__class__");
536+
/* XXX Sometimes this is null! Like after "class C: pass",
537+
C.__class__ raises AttributeError. Don't know of other
538+
cases. */
539+
if (itsclass == NULL)
540+
PyErr_Clear();
541+
else {
542+
int status = merge_class_dict(masterdict, itsclass);
543+
Py_DECREF(itsclass);
544+
if (status < 0)
545+
goto error;
484546
}
485547
}
486-
if (PyList_Sort(l) != 0)
548+
549+
assert((result == NULL) ^ (masterdict == NULL));
550+
if (masterdict != NULL) {
551+
/* The result comes from its keys. */
552+
assert(result == NULL);
553+
result = PyMapping_Keys(masterdict);
554+
if (result == NULL)
555+
goto error;
556+
}
557+
558+
assert(result);
559+
if (PyList_Sort(result) != 0)
487560
goto error;
488-
return l;
561+
else
562+
goto normal_return;
563+
489564
error:
490-
Py_XDECREF(l);
491-
return NULL;
565+
Py_XDECREF(result);
566+
result = NULL;
567+
/* fall through */
568+
normal_return:
569+
Py_XDECREF(masterdict);
570+
return result;
492571
}
493572

494573
static char dir_doc[] =
495-
"dir([object]) -> list of strings\n\
496-
\n\
497-
Return an alphabetized list of names comprising (some of) the attributes\n\
498-
of the given object. Without an argument, the names in the current scope\n\
499-
are listed. With an instance argument, only the instance attributes are\n\
500-
returned. With a class argument, attributes of the base class are not\n\
501-
returned. For other types or arguments, this may list members or methods.";
502-
574+
"dir([object]) -> list of strings\n"
575+
"\n"
576+
"Return an alphabetized list of names comprising (some of) the attributes\n"
577+
"of the given object, and of attributes reachable from it:\n"
578+
"\n"
579+
"No argument: the names in the current scope.\n"
580+
"Module object: the module attributes.\n"
581+
"Type object: its attributes, and recursively the attributes of its bases.\n"
582+
"Otherwise: its attributes, its class's attributes, and recursively the\n"
583+
" attributes of its class's base classes.";
503584

504585
static PyObject *
505586
builtin_divmod(PyObject *self, PyObject *args)

0 commit comments

Comments
 (0)