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

Skip to content

Commit e32b422

Browse files
committed
Patch #1591665: implement the __dir__() special function lookup in PyObject_Dir.
1 parent af33438 commit e32b422

5 files changed

Lines changed: 259 additions & 162 deletions

File tree

Doc/lib/libfuncs.tex

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -274,35 +274,54 @@ \section{Built-in Functions \label{built-in-funcs}}
274274
\end{funcdesc}
275275

276276
\begin{funcdesc}{dir}{\optional{object}}
277-
Without arguments, return the list of names in the current local
278-
symbol table. With an argument, attempts to return a list of valid
279-
attributes for that object. This information is gleaned from the
280-
object's \member{__dict__} attribute, if defined, and from the class
281-
or type object. The list is not necessarily complete.
282-
If the object is a module object, the list contains the names of the
283-
module's attributes.
284-
If the object is a type or class object,
285-
the list contains the names of its attributes,
286-
and recursively of the attributes of its bases.
287-
Otherwise, the list contains the object's attributes' names,
288-
the names of its class's attributes,
289-
and recursively of the attributes of its class's base classes.
290-
The resulting list is sorted alphabetically.
291-
For example:
277+
Without arguments, return the list of names in the current local scope. With
278+
an argument, attempt to return a list of valid attributes for that object.
279+
280+
If the object has a method named \method{__dir__()}, this method will be
281+
called and must return the list of attributes. This allows objects that
282+
implement a custom \function{__getattr__()} or \function{__getattribute__()}
283+
function to customize the way \function{dir()} reports their attributes.
284+
285+
If the object does not provide \method{__dir__()}, the function tries its best
286+
to gather information from the object's \member{__dict__} attribute, if
287+
defined, and from its type object. The resulting list is not necessarily
288+
complete, and may be inaccurate when the object has a custom
289+
\function{__getattr__()}.
290+
291+
The default \function{dir()} mechanism behaves differently with different
292+
types of objects, as it attempts to produce the most relevant, rather than
293+
complete, information:
294+
\begin{itemize}
295+
\item If the object is a module object, the list contains the names of the
296+
module's attributes.
297+
\item If the object is a type or class object, the list contains the names of
298+
its attributes, and recursively of the attributes of its bases.
299+
\item Otherwise, the list contains the object's attributes' names, the names
300+
of its class's attributes, and recursively of the attributes of its class's
301+
base classes.
302+
\end{itemize}
303+
304+
The resulting list is sorted alphabetically. For example:
292305

293306
\begin{verbatim}
294307
>>> import struct
295308
>>> dir()
296309
['__builtins__', '__doc__', '__name__', 'struct']
297310
>>> dir(struct)
298311
['__doc__', '__name__', 'calcsize', 'error', 'pack', 'unpack']
312+
>>> class Foo(object):
313+
... def __dir__(self):
314+
... return ["kan", "ga", "roo"]
315+
...
316+
>>> f = Foo()
317+
>>> dir(f)
318+
['ga', 'kan', 'roo']
299319
\end{verbatim}
300320

301-
\note{Because \function{dir()} is supplied primarily as a convenience
302-
for use at an interactive prompt,
303-
it tries to supply an interesting set of names more than it tries to
304-
supply a rigorously or consistently defined set of names,
305-
and its detailed behavior may change across releases.}
321+
\note{Because \function{dir()} is supplied primarily as a convenience for use
322+
at an interactive prompt, it tries to supply an interesting set of names
323+
more than it tries to supply a rigorously or consistently defined set of
324+
names, and its detailed behavior may change across releases.}
306325
\end{funcdesc}
307326

308327
\begin{funcdesc}{divmod}{a, b}

Lib/test/test_builtin.py

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,67 @@ def test_delattr(self):
223223
self.assertRaises(TypeError, delattr)
224224

225225
def test_dir(self):
226-
x = 1
227-
self.assert_('x' in dir())
228-
import sys
229-
self.assert_('modules' in dir(sys))
226+
# dir(wrong number of arguments)
230227
self.assertRaises(TypeError, dir, 42, 42)
231228

229+
# dir() - local scope
230+
local_var = 1
231+
self.assert_('local_var' in dir())
232+
233+
# dir(module)
234+
import sys
235+
self.assert_('exit' in dir(sys))
236+
237+
# dir(module_with_invalid__dict__)
238+
import types
239+
class Foo(types.ModuleType):
240+
__dict__ = 8
241+
f = Foo("foo")
242+
self.assertRaises(TypeError, dir, f)
243+
244+
# dir(type)
245+
self.assert_("strip" in dir(str))
246+
self.assert_("__mro__" not in dir(str))
247+
248+
# dir(obj)
249+
class Foo(object):
250+
def __init__(self):
251+
self.x = 7
252+
self.y = 8
253+
self.z = 9
254+
f = Foo()
255+
self.assert_("y" in dir(f))
256+
257+
# dir(obj_no__dict__)
258+
class Foo(object):
259+
__slots__ = []
260+
f = Foo()
261+
self.assert_("__repr__" in dir(f))
262+
263+
# dir(obj_no__class__with__dict__)
264+
# (an ugly trick to cause getattr(f, "__class__") to fail)
265+
class Foo(object):
266+
__slots__ = ["__class__", "__dict__"]
267+
def __init__(self):
268+
self.bar = "wow"
269+
f = Foo()
270+
self.assert_("__repr__" not in dir(f))
271+
self.assert_("bar" in dir(f))
272+
273+
# dir(obj_using __dir__)
274+
class Foo(object):
275+
def __dir__(self):
276+
return ["kan", "ga", "roo"]
277+
f = Foo()
278+
self.assert_(dir(f) == ["ga", "kan", "roo"])
279+
280+
# dir(obj__dir__not_list)
281+
class Foo(object):
282+
def __dir__(self):
283+
return 7
284+
f = Foo()
285+
self.assertRaises(TypeError, dir, f)
286+
232287
def test_divmod(self):
233288
self.assertEqual(divmod(12, 7), (1, 5))
234289
self.assertEqual(divmod(-12, 7), (-2, 2))

Misc/NEWS

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ TO DO
2828
Core and Builtins
2929
-----------------
3030

31-
- Removing indexing/slicing on BaseException.
31+
- The dir() function has been extended to call the __dir__() method on
32+
its argument, if it exists. If not, it will work like before. This allows
33+
customizing the output of dir() in the presence of a __getattr__().
3234

33-
- Remove the exceptions module, all the exceptions are already builtin.
35+
- Removed indexing/slicing on BaseException.
3436

35-
- input() becomes raw_input(): the name input() now implements the
37+
- Removed the exceptions module, all the exceptions are already builtin.
38+
39+
- input() became raw_input(): the name input() now implements the
3640
functionality formerly known as raw_input(); the name raw_input()
3741
is no longer defined.
3842

0 commit comments

Comments
 (0)