@@ -426,80 +426,161 @@ the effects of any future statements in effect in the code calling\n\
426426compile; if absent or zero these statements do influence the compilation,\n\
427427in 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
430477static PyObject *
431478builtin_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
494573static 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
504585static PyObject *
505586builtin_divmod (PyObject * self , PyObject * args )
0 commit comments