mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Patch #1591665: implement the __dir__() special function lookup in PyObject_Dir.
This commit is contained in:
		
							parent
							
								
									af334387d1
								
							
						
					
					
						commit
						e32b4224d0
					
				
					 5 changed files with 266 additions and 169 deletions
				
			
		|  | @ -274,21 +274,34 @@ class C: | ||||||
| \end{funcdesc} | \end{funcdesc} | ||||||
| 
 | 
 | ||||||
| \begin{funcdesc}{dir}{\optional{object}} | \begin{funcdesc}{dir}{\optional{object}} | ||||||
|   Without arguments, return the list of names in the current local |   Without arguments, return the list of names in the current local scope.  With | ||||||
|   symbol table.  With an argument, attempts to return a list of valid |   an argument, attempt to return a list of valid attributes for that object. | ||||||
|   attributes for that object.  This information is gleaned from the | 
 | ||||||
|   object's \member{__dict__} attribute, if defined, and from the class |   If the object has a method named \method{__dir__()}, this method will be | ||||||
|   or type object.  The list is not necessarily complete. |   called and must return the list of attributes. This allows objects that | ||||||
|   If the object is a module object, the list contains the names of the |   implement a custom \function{__getattr__()} or \function{__getattribute__()} | ||||||
|  |   function to customize the way \function{dir()} reports their attributes. | ||||||
|  | 
 | ||||||
|  |   If the object does not provide \method{__dir__()}, the function tries its best | ||||||
|  |   to gather information from the object's \member{__dict__} attribute, if | ||||||
|  |   defined, and from its type object.  The resulting list is not necessarily | ||||||
|  |   complete, and may be inaccurate when the object has a custom | ||||||
|  |   \function{__getattr__()}. | ||||||
|  |    | ||||||
|  |   The default \function{dir()} mechanism behaves differently with different | ||||||
|  |   types of objects, as it attempts to produce the most relevant, rather than | ||||||
|  |   complete, information: | ||||||
|  |   \begin{itemize} | ||||||
|  |   \item If the object is a module object, the list contains the names of the | ||||||
|     module's attributes. |     module's attributes. | ||||||
|   If the object is a type or class object, |   \item If the object is a type or class object, the list contains the names of | ||||||
|   the list contains the names of its attributes, |     its attributes, and recursively of the attributes of its bases. | ||||||
|   and recursively of the attributes of its bases. |   \item Otherwise, the list contains the object's attributes' names, the names | ||||||
|   Otherwise, the list contains the object's attributes' names, |     of its class's attributes, and recursively of the attributes of its class's | ||||||
|   the names of its class's attributes, |     base classes. | ||||||
|   and recursively of the attributes of its class's base classes. |   \end{itemize} | ||||||
|   The resulting list is sorted alphabetically. |    | ||||||
|   For example: |   The resulting list is sorted alphabetically.  For example: | ||||||
| 
 | 
 | ||||||
| \begin{verbatim} | \begin{verbatim} | ||||||
| >>> import struct | >>> import struct | ||||||
|  | @ -296,13 +309,19 @@ class C: | ||||||
| ['__builtins__', '__doc__', '__name__', 'struct'] | ['__builtins__', '__doc__', '__name__', 'struct'] | ||||||
| >>> dir(struct) | >>> dir(struct) | ||||||
| ['__doc__', '__name__', 'calcsize', 'error', 'pack', 'unpack'] | ['__doc__', '__name__', 'calcsize', 'error', 'pack', 'unpack'] | ||||||
|  | >>> class Foo(object): | ||||||
|  | ...     def __dir__(self): | ||||||
|  | ...         return ["kan", "ga", "roo"] | ||||||
|  | ... | ||||||
|  | >>> f = Foo() | ||||||
|  | >>> dir(f) | ||||||
|  | ['ga', 'kan', 'roo'] | ||||||
| \end{verbatim} | \end{verbatim} | ||||||
| 
 | 
 | ||||||
|   \note{Because \function{dir()} is supplied primarily as a convenience |   \note{Because \function{dir()} is supplied primarily as a convenience for use | ||||||
|   for use at an interactive prompt, |     at an interactive prompt, it tries to supply an interesting set of names | ||||||
|   it tries to supply an interesting set of names more than it tries to |     more than it tries to supply a rigorously or consistently defined set of | ||||||
|   supply a rigorously or consistently defined set of names, |     names, and its detailed behavior may change across releases.} | ||||||
|   and its detailed behavior may change across releases.} |  | ||||||
| \end{funcdesc} | \end{funcdesc} | ||||||
| 
 | 
 | ||||||
| \begin{funcdesc}{divmod}{a, b} | \begin{funcdesc}{divmod}{a, b} | ||||||
|  |  | ||||||
|  | @ -223,12 +223,67 @@ def test_delattr(self): | ||||||
|         self.assertRaises(TypeError, delattr) |         self.assertRaises(TypeError, delattr) | ||||||
| 
 | 
 | ||||||
|     def test_dir(self): |     def test_dir(self): | ||||||
|         x = 1 |         # dir(wrong number of arguments) | ||||||
|         self.assert_('x' in dir()) |  | ||||||
|         import sys |  | ||||||
|         self.assert_('modules' in dir(sys)) |  | ||||||
|         self.assertRaises(TypeError, dir, 42, 42) |         self.assertRaises(TypeError, dir, 42, 42) | ||||||
| 
 | 
 | ||||||
|  |         # dir() - local scope | ||||||
|  |         local_var = 1 | ||||||
|  |         self.assert_('local_var' in dir()) | ||||||
|  | 
 | ||||||
|  |         # dir(module) | ||||||
|  |         import sys | ||||||
|  |         self.assert_('exit' in dir(sys)) | ||||||
|  | 
 | ||||||
|  |         # dir(module_with_invalid__dict__) | ||||||
|  |         import types | ||||||
|  |         class Foo(types.ModuleType): | ||||||
|  |             __dict__ = 8 | ||||||
|  |         f = Foo("foo") | ||||||
|  |         self.assertRaises(TypeError, dir, f) | ||||||
|  | 
 | ||||||
|  |         # dir(type) | ||||||
|  |         self.assert_("strip" in dir(str)) | ||||||
|  |         self.assert_("__mro__" not in dir(str)) | ||||||
|  | 
 | ||||||
|  |         # dir(obj) | ||||||
|  |         class Foo(object): | ||||||
|  |             def __init__(self): | ||||||
|  |                 self.x = 7 | ||||||
|  |                 self.y = 8 | ||||||
|  |                 self.z = 9 | ||||||
|  |         f = Foo() | ||||||
|  |         self.assert_("y" in dir(f)) | ||||||
|  | 
 | ||||||
|  |         # dir(obj_no__dict__) | ||||||
|  |         class Foo(object): | ||||||
|  |             __slots__ = [] | ||||||
|  |         f = Foo() | ||||||
|  |         self.assert_("__repr__" in dir(f)) | ||||||
|  | 
 | ||||||
|  |         # dir(obj_no__class__with__dict__) | ||||||
|  |         # (an ugly trick to cause getattr(f, "__class__") to fail) | ||||||
|  |         class Foo(object): | ||||||
|  |             __slots__ = ["__class__", "__dict__"] | ||||||
|  |             def __init__(self): | ||||||
|  |                 self.bar = "wow" | ||||||
|  |         f = Foo() | ||||||
|  |         self.assert_("__repr__" not in dir(f)) | ||||||
|  |         self.assert_("bar" in dir(f)) | ||||||
|  | 
 | ||||||
|  |         # dir(obj_using __dir__) | ||||||
|  |         class Foo(object): | ||||||
|  |             def __dir__(self): | ||||||
|  |                 return ["kan", "ga", "roo"] | ||||||
|  |         f = Foo() | ||||||
|  |         self.assert_(dir(f) == ["ga", "kan", "roo"]) | ||||||
|  | 
 | ||||||
|  |         # dir(obj__dir__not_list) | ||||||
|  |         class Foo(object): | ||||||
|  |             def __dir__(self): | ||||||
|  |                 return 7 | ||||||
|  |         f = Foo() | ||||||
|  |         self.assertRaises(TypeError, dir, f) | ||||||
|  | 
 | ||||||
|     def test_divmod(self): |     def test_divmod(self): | ||||||
|         self.assertEqual(divmod(12, 7), (1, 5)) |         self.assertEqual(divmod(12, 7), (1, 5)) | ||||||
|         self.assertEqual(divmod(-12, 7), (-2, 2)) |         self.assertEqual(divmod(-12, 7), (-2, 2)) | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								Misc/NEWS
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								Misc/NEWS
									
										
									
									
									
								
							|  | @ -28,11 +28,15 @@ TO DO | ||||||
| Core and Builtins | Core and Builtins | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
| - Removing indexing/slicing on BaseException. | - The dir() function has been extended to call the __dir__() method on | ||||||
|  |   its argument, if it exists. If not, it will work like before. This allows | ||||||
|  |   customizing the output of dir() in the presence of a __getattr__(). | ||||||
| 
 | 
 | ||||||
| - Remove the exceptions module, all the exceptions are already builtin. | - Removed indexing/slicing on BaseException. | ||||||
| 
 | 
 | ||||||
| - input() becomes raw_input(): the name input() now implements the | - Removed the exceptions module, all the exceptions are already builtin. | ||||||
|  | 
 | ||||||
|  | - input() became raw_input(): the name input() now implements the | ||||||
|   functionality formerly known as raw_input(); the name raw_input() |   functionality formerly known as raw_input(); the name raw_input() | ||||||
|   is no longer defined. |   is no longer defined. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										264
									
								
								Objects/object.c
									
										
									
									
									
								
							
							
						
						
									
										264
									
								
								Objects/object.c
									
										
									
									
									
								
							|  | @ -1284,6 +1284,8 @@ PyCallable_Check(PyObject *x) | ||||||
| 	return x->ob_type->tp_call != NULL; | 	return x->ob_type->tp_call != NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* ------------------------- PyObject_Dir() helpers ------------------------- */ | ||||||
|  | 
 | ||||||
| /* Helper for PyObject_Dir.
 | /* Helper for PyObject_Dir.
 | ||||||
|    Merge the __dict__ of aclass into dict, and recursively also all |    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 |    the __dict__s of aclass's base classes.  The order of merging isn't | ||||||
|  | @ -1343,158 +1345,174 @@ merge_class_dict(PyObject* dict, PyObject* aclass) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Helper for PyObject_Dir.
 | /* Helper for PyObject_Dir without arguments: returns the local scope. */ | ||||||
|    If obj has an attr named attrname that's a list, merge its string | static PyObject * | ||||||
|    elements into keys of dict. | _dir_locals() | ||||||
|    Return 0 on success, -1 on error.  Errors due to not finding the attr, |  | ||||||
|    or the attr not being a list, are suppressed. |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| static int |  | ||||||
| merge_list_attr(PyObject* dict, PyObject* obj, const char *attrname) |  | ||||||
| { | { | ||||||
| 	PyObject *list; | 	PyObject *locals = PyEval_GetLocals(); | ||||||
| 	int result = 0; |  | ||||||
| 
 | 
 | ||||||
| 	assert(PyDict_Check(dict)); | 	if (locals == NULL) { | ||||||
| 	assert(obj); | 		PyErr_SetString(PyExc_SystemError, "frame does not exist"); | ||||||
| 	assert(attrname); | 		return NULL; | ||||||
| 
 |  | ||||||
| 	list = PyObject_GetAttrString(obj, attrname); |  | ||||||
| 	if (list == NULL) |  | ||||||
| 		PyErr_Clear(); |  | ||||||
| 
 |  | ||||||
| 	else if (PyList_Check(list)) { |  | ||||||
| 		int i; |  | ||||||
| 		for (i = 0; i < PyList_GET_SIZE(list); ++i) { |  | ||||||
| 			PyObject *item = PyList_GET_ITEM(list, i); |  | ||||||
| 			if (PyString_Check(item)) { |  | ||||||
| 				result = PyDict_SetItem(dict, item, Py_None); |  | ||||||
| 				if (result < 0) |  | ||||||
| 					break; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	Py_XDECREF(list); | 	/* the locals don't need to be DECREF'd */ | ||||||
|  | 	return PyMapping_Keys(locals); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Helper for PyObject_Dir of type objects: returns __dict__ and __bases__.
 | ||||||
|  |    We deliberately don't suck up its __class__, as methods belonging to the  | ||||||
|  |    metaclass would probably be more confusing than helpful.  | ||||||
|  | */ | ||||||
|  | static PyObject *  | ||||||
|  | _specialized_dir_type(PyObject *obj) | ||||||
|  | { | ||||||
|  | 	PyObject *result = NULL; | ||||||
|  | 	PyObject *dict = PyDict_New(); | ||||||
|  | 
 | ||||||
|  | 	if (dict != NULL && merge_class_dict(dict, obj) == 0) | ||||||
|  | 		result = PyDict_Keys(dict); | ||||||
|  | 
 | ||||||
|  | 	Py_XDECREF(dict); | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Like __builtin__.dir(arg).  See bltinmodule.c's builtin_dir for the
 | /* Helper for PyObject_Dir of module objects: returns the module's __dict__. */ | ||||||
|    docstring, which should be kept in synch with this implementation. */ | static PyObject * | ||||||
| 
 | _specialized_dir_module(PyObject *obj) | ||||||
| PyObject * |  | ||||||
| PyObject_Dir(PyObject *arg) |  | ||||||
| { | { | ||||||
| 	/* Set exactly one of these non-NULL before the end. */ | 	PyObject *result = NULL; | ||||||
| 	PyObject *result = NULL;	/* result list */ | 	PyObject *dict = PyObject_GetAttrString(obj, "__dict__"); | ||||||
| 	PyObject *masterdict = NULL;	/* result is masterdict.keys() */ |  | ||||||
| 
 | 
 | ||||||
| 	/* If NULL arg, return the locals. */ | 	if (dict != NULL) { | ||||||
| 	if (arg == NULL) { | 		if (PyDict_Check(dict)) | ||||||
| 		PyObject *locals = PyEval_GetLocals(); | 			result = PyDict_Keys(dict); | ||||||
| 		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 (PyModule_Check(arg)) { |  | ||||||
| 		masterdict = PyObject_GetAttrString(arg, "__dict__"); |  | ||||||
| 		if (masterdict == NULL) |  | ||||||
| 			goto error; |  | ||||||
| 		if (!PyDict_Check(masterdict)) { |  | ||||||
| 			PyErr_SetString(PyExc_TypeError, |  | ||||||
| 					"module.__dict__ is not a dictionary"); |  | ||||||
| 			goto error; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/* Elif some form of type or class, grab its dict and its bases.
 |  | ||||||
| 	   We deliberately don't suck up its __class__, as methods belonging |  | ||||||
| 	   to the metaclass would probably be more confusing than helpful. */ |  | ||||||
| 	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 { | 		else { | ||||||
| 		PyObject *itsclass; | 			PyErr_Format(PyExc_TypeError, | ||||||
| 		/* Create a dict to start with.  CAUTION:  Not everything
 | 				     "%.200s.__dict__ is not a dictionary", | ||||||
| 		   responding to __dict__ returns a dict! */ | 				     PyModule_GetName(obj)); | ||||||
| 		masterdict = PyObject_GetAttrString(arg, "__dict__"); | 		} | ||||||
| 		if (masterdict == NULL) { | 	} | ||||||
|  | 
 | ||||||
|  | 	Py_XDECREF(dict); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Helper for PyObject_Dir of generic objects: returns __dict__, __class__,
 | ||||||
|  |    and recursively up the __class__.__bases__ chain. | ||||||
|  | */ | ||||||
|  | static PyObject * | ||||||
|  | _generic_dir(PyObject *obj) | ||||||
|  | { | ||||||
|  | 	PyObject *result = NULL; | ||||||
|  | 	PyObject *dict = NULL; | ||||||
|  | 	PyObject *itsclass = NULL; | ||||||
|  | 	 | ||||||
|  | 	/* Get __dict__ (which may or may not be a real dict...) */ | ||||||
|  | 	dict = PyObject_GetAttrString(obj, "__dict__"); | ||||||
|  | 	if (dict == NULL) { | ||||||
| 		PyErr_Clear(); | 		PyErr_Clear(); | ||||||
| 			masterdict = PyDict_New(); | 		dict = PyDict_New(); | ||||||
| 	} | 	} | ||||||
| 		else if (!PyDict_Check(masterdict)) { | 	else if (!PyDict_Check(dict)) { | ||||||
| 			Py_DECREF(masterdict); | 		Py_DECREF(dict); | ||||||
| 			masterdict = PyDict_New(); | 		dict = PyDict_New(); | ||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
| 			/* The object may have returned a reference to its
 | 		/* Copy __dict__ to avoid mutating it. */ | ||||||
| 			   dict, so copy it to avoid mutating it. */ | 		PyObject *temp = PyDict_Copy(dict); | ||||||
| 			PyObject *temp = PyDict_Copy(masterdict); | 		Py_DECREF(dict); | ||||||
| 			Py_DECREF(masterdict); | 		dict = temp; | ||||||
| 			masterdict = temp; |  | ||||||
| 	} | 	} | ||||||
| 		if (masterdict == NULL) | 
 | ||||||
|  | 	if (dict == NULL) | ||||||
| 		goto error; | 		goto error; | ||||||
| 
 | 
 | ||||||
| 		/* Merge in __members__ and __methods__ (if any).
 | 	/* Merge in attrs reachable from its class. */ | ||||||
| 		   XXX Would like this to go away someday; for now, it's | 	itsclass = PyObject_GetAttrString(obj, "__class__"); | ||||||
| 		   XXX needed to get at im_self etc of method objects. */ |  | ||||||
| 		if (merge_list_attr(masterdict, arg, "__members__") < 0) |  | ||||||
| 			goto error; |  | ||||||
| 		if (merge_list_attr(masterdict, arg, "__methods__") < 0) |  | ||||||
| 			goto error; |  | ||||||
| 
 |  | ||||||
| 		/* Merge in attrs reachable from its class.
 |  | ||||||
| 		   CAUTION:  Not all objects have a __class__ attr. */ |  | ||||||
| 		itsclass = PyObject_GetAttrString(arg, "__class__"); |  | ||||||
| 	if (itsclass == NULL) | 	if (itsclass == NULL) | ||||||
|  | 		/* XXX(tomer): Perhaps fall back to obj->ob_type if no
 | ||||||
|  | 		               __class__ exists? */ | ||||||
| 		PyErr_Clear(); | 		PyErr_Clear(); | ||||||
| 	else { | 	else { | ||||||
| 			int status = merge_class_dict(masterdict, itsclass); | 		if (merge_class_dict(dict, itsclass) != 0) | ||||||
| 			Py_DECREF(itsclass); |  | ||||||
| 			if (status < 0) |  | ||||||
| 			goto error; | 			goto error; | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	assert((result == NULL) ^ (masterdict == NULL)); | 	result = PyDict_Keys(dict); | ||||||
| 	if (masterdict != NULL) { | 	/* fall through */ | ||||||
| 		/* The result comes from its keys. */ | error: | ||||||
| 		assert(result == NULL); | 	Py_XDECREF(itsclass); | ||||||
| 		result = PyDict_Keys(masterdict); | 	Py_XDECREF(dict); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Helper for PyObject_Dir: object introspection.
 | ||||||
|  |    This calls one of the above specialized versions if no __dir__ method | ||||||
|  |    exists. */ | ||||||
|  | static PyObject * | ||||||
|  | _dir_object(PyObject *obj) | ||||||
|  | { | ||||||
|  | 	PyObject * result = NULL; | ||||||
|  | 	PyObject * dirfunc = PyObject_GetAttrString((PyObject*)obj->ob_type, | ||||||
|  | 						    "__dir__"); | ||||||
|  | 
 | ||||||
|  | 	assert(obj); | ||||||
|  | 	if (dirfunc == NULL) { | ||||||
|  | 		/* use default implementation */ | ||||||
|  | 		PyErr_Clear(); | ||||||
|  | 		if (PyModule_Check(obj)) | ||||||
|  | 			result = _specialized_dir_module(obj); | ||||||
|  | 		else if (PyType_Check(obj)) | ||||||
|  | 			result = _specialized_dir_type(obj); | ||||||
|  | 		else | ||||||
|  | 			result = _generic_dir(obj); | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
|  | 		/* use __dir__ */ | ||||||
|  | 		result = PyObject_CallFunctionObjArgs(dirfunc, obj, NULL); | ||||||
|  | 		Py_DECREF(dirfunc); | ||||||
| 		if (result == NULL) | 		if (result == NULL) | ||||||
| 			goto error; | 			return NULL; | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	assert(result); | 		/* result must be a list */ | ||||||
|  | 		/* XXX(gbrandl): could also check if all items are strings */ | ||||||
| 		if (!PyList_Check(result)) { | 		if (!PyList_Check(result)) { | ||||||
| 			PyErr_Format(PyExc_TypeError, | 			PyErr_Format(PyExc_TypeError, | ||||||
| 			"Expected keys() to be a list, not '%.200s'", | 				     "__dir__() must return a list, not %.200s", | ||||||
| 				     result->ob_type->tp_name); | 				     result->ob_type->tp_name); | ||||||
| 		goto error; | 			Py_DECREF(result); | ||||||
| 	} |  | ||||||
| 	if (PyList_Sort(result) != 0) |  | ||||||
| 		goto error; |  | ||||||
| 	else |  | ||||||
| 		goto normal_return; |  | ||||||
| 
 |  | ||||||
|   error: |  | ||||||
| 	Py_XDECREF(result); |  | ||||||
| 			result = NULL; | 			result = NULL; | ||||||
| 	/* fall through */ | 		} | ||||||
|   normal_return: | 	} | ||||||
|   	Py_XDECREF(masterdict); | 
 | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Implementation of dir() -- if obj is NULL, returns the names in the current
 | ||||||
|  |    (local) scope.  Otherwise, performs introspection of the object: returns a | ||||||
|  |    sorted list of attribute names (supposedly) accessible from the object | ||||||
|  | */ | ||||||
|  | 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 result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -427,15 +427,16 @@ builtin_dir(PyObject *self, PyObject *args) | ||||||
| PyDoc_STRVAR(dir_doc, | PyDoc_STRVAR(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" | "If called without an argument, return the names in the current scope.\n" | ||||||
| "of the given object, and of attributes reachable from it:\n" | "Else, return an alphabetized list of names comprising (some of) the attributes\n" | ||||||
| "\n" | "of the given object, and of attributes reachable from it.\n" | ||||||
| "No argument:  the names in the current scope.\n" | "If the object supplies a method named __dir__, it will be used; otherwise\n" | ||||||
| "Module object:  the module attributes.\n" | "the default dir() logic is used and returns:\n" | ||||||
| "Type or class object:  its attributes, and recursively the attributes of\n" | "  for a module object: the module's attributes.\n" | ||||||
| "    its bases.\n" | "  for a class object:  its attributes, and recursively the attributes\n" | ||||||
| "Otherwise:  its attributes, its class's attributes, and recursively the\n" | "    of its bases.\n" | ||||||
| "    attributes of its class's base classes."); | "  for an other object: its attributes, its class's attributes, and\n" | ||||||
|  | "    recursively the 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
	
	 Georg Brandl
						Georg Brandl