[Python-checkins] CVS: python/dist/src/Python bltinmodule.c,2.231,2.232

Tim Peters tim_one@users.sourceforge.net
Sun, 02 Sep 2001 22:47:41 -0700


Update of /cvsroot/python/python/dist/src/Python
In directory usw-pr-cvs1:/tmp/cvs-serv12547/python/Python

Modified Files:
	bltinmodule.c 
Log Message:
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.


Index: bltinmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/bltinmodule.c,v
retrieving revision 2.231
retrieving revision 2.232
diff -C2 -d -r2.231 -r2.232
*** bltinmodule.c	2001/08/24 16:52:18	2.231
--- bltinmodule.c	2001/09/03 05:47:38	2.232
***************
*** 427,504 ****
  in addition to any features explicitly specified.";
  
  
  static PyObject *
  builtin_dir(PyObject *self, PyObject *args)
  {
! 	static char *attrlist[] = {"__members__", "__methods__", NULL};
! 	PyObject *v = NULL, *l = NULL, *m = NULL;
! 	PyObject *d, *x;
! 	int i;
! 	char **s;
  
! 	if (!PyArg_ParseTuple(args, "|O:dir", &v))
  		return NULL;
! 	if (v == NULL) {
! 		x = PyEval_GetLocals();
! 		if (x == NULL)
  			goto error;
! 		l = PyMapping_Keys(x);
! 		if (l == NULL)
  			goto error;
  	}
  	else {
! 		d = PyObject_GetAttrString(v, "__dict__");
! 		if (d == NULL)
  			PyErr_Clear();
! 		else {
! 			l = PyMapping_Keys(d);
! 			if (l == NULL)
! 				PyErr_Clear();
! 			Py_DECREF(d);
  		}
! 		if (l == NULL) {
! 			l = PyList_New(0);
! 			if (l == NULL)
  				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);
! 				if (x == NULL) {
! 					PyErr_Clear();
! 					break;
! 				}
! 				if (PyList_Append(l, x) != 0) {
! 					Py_DECREF(x);
! 					Py_DECREF(m);
! 					goto error;
! 				}
! 				Py_DECREF(x);
! 			}
! 			Py_DECREF(m);
  		}
  	}
! 	if (PyList_Sort(l) != 0)
  		goto error;
! 	return l;
    error:
! 	Py_XDECREF(l);
! 	return NULL;
  }
  
  static char dir_doc[] =
! "dir([object]) -> list of strings\n\
! \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\
! are listed.  With an instance argument, only the instance attributes are\n\
! returned.  With a class argument, attributes of the base class are not\n\
! returned.  For other types or arguments, this may list members or methods.";
! 
  
  static PyObject *
--- 427,585 ----
  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 *
  builtin_dir(PyObject *self, PyObject *args)
  {
! 	PyObject *arg = NULL;
! 	/* Set exactly one of these non-NULL before the end. */
! 	PyObject *result = NULL;	/* result list */
! 	PyObject *masterdict = NULL;	/* result is masterdict.keys() */
  
! 	if (!PyArg_ParseTuple(args, "|O:dir", &arg))
  		return NULL;
! 
! 	/* If no arg, return the locals. */
! 	if (arg == NULL) {
! 		PyObject *locals = PyEval_GetLocals();
! 		if (locals == NULL)
  			goto error;
! 		result = PyMapping_Keys(locals);
! 		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;
  		}
! 		else {
! 			/* The object may have returned a reference to its
! 			   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();
! 		else {
! 			int status = merge_class_dict(masterdict, itsclass);
! 			Py_DECREF(itsclass);
! 			if (status < 0)
! 				goto error;
  		}
  	}
! 
! 	assert((result == NULL) ^ (masterdict == NULL));
! 	if (masterdict != NULL) {
! 		/* The result comes from its keys. */
! 		assert(result == NULL);
! 		result = PyMapping_Keys(masterdict);
! 		if (result == NULL)
! 			goto error;
! 	}
! 
! 	assert(result);
! 	if (PyList_Sort(result) != 0)
  		goto error;
! 	else
! 		goto normal_return;
! 
    error:
! 	Py_XDECREF(result);
! 	result = NULL;
! 	/* fall through */
!   normal_return:
!   	Py_XDECREF(masterdict);
! 	return result;
  }
  
  static char dir_doc[] =
! "dir([object]) -> list of strings\n"
! "\n"
! "Return an alphabetized list of names comprising (some of) the attributes\n"
! "of the given object, and of attributes reachable from it:\n"
! "\n"
! "No argument:  the names in the current scope.\n"
! "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 *