Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
e32b4224
Commit
e32b4224
authored
Mar 10, 2007
by
Georg Brandl
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Patch #
15916658
: implement the __dir__() special function lookup in PyObject_Dir.
parent
af334387
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
259 additions
and
162 deletions
+259
-162
Doc/lib/libfuncs.tex
Doc/lib/libfuncs.tex
+39
-20
Lib/test/test_builtin.py
Lib/test/test_builtin.py
+59
-4
Misc/NEWS
Misc/NEWS
+7
-3
Objects/object.c
Objects/object.c
+144
-126
Python/bltinmodule.c
Python/bltinmodule.c
+10
-9
No files found.
Doc/lib/libfuncs.tex
View file @
e32b4224
...
@@ -274,21 +274,34 @@ class C:
...
@@ -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
__
()
}
module's attributes.
function to customize the way
\function
{
dir()
}
reports their attributes.
If the object is a type or class object,
the list contains the names of its attributes,
If the object does not provide
\method
{__
dir
__
()
}
, the function tries its best
and recursively of the attributes of its bases.
to gather information from the object's
\member
{__
dict
__}
attribute, if
Otherwise, the list contains the object's attributes' names,
defined, and from its type object. The resulting list is not necessarily
the names of its class's attributes,
complete, and may be inaccurate when the object has a custom
and recursively of the attributes of its class's base classes.
\function
{__
getattr
__
()
}
.
The resulting list is sorted alphabetically.
For example:
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.
\item
If the object is a type or class object, the list contains the names of
its attributes, and recursively of the attributes of its bases.
\item
Otherwise, the list contains the object's attributes' names, the names
of its class's attributes, and recursively of the attributes of its class's
base classes.
\end{itemize}
The resulting list is sorted alphabetically. For example:
\begin{verbatim}
\begin{verbatim}
>>> import struct
>>> import struct
...
@@ -296,13 +309,19 @@ class C:
...
@@ -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
}
...
...
Lib/test/test_builtin.py
View file @
e32b4224
...
@@ -223,12 +223,67 @@ class BuiltinTest(unittest.TestCase):
...
@@ -223,12 +223,67 @@ class BuiltinTest(unittest.TestCase):
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
))
...
...
Misc/NEWS
View file @
e32b4224
...
@@ -28,11 +28,15 @@ TO DO
...
@@ -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 builti
n.
- Remove
d indexing/slicing on BaseExceptio
n.
- 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.
...
...
Objects/object.c
View file @
e32b4224
...
@@ -1284,6 +1284,8 @@ PyCallable_Check(PyObject *x)
...
@@ -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)
...
@@ -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.
PyObject
*
locals
=
PyEval_GetLocals
();
*/
static
int
if
(
locals
==
NULL
)
{
merge_list_attr
(
PyObject
*
dict
,
PyObject
*
obj
,
const
char
*
attrname
)
PyErr_SetString
(
PyExc_SystemError
,
"frame does not exist"
);
return
NULL
;
}
/* 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
*
list
;
PyObject
*
result
=
NULL
;
int
result
=
0
;
PyObject
*
dict
=
PyDict_New
()
;
assert
(
PyDict_Check
(
dict
));
if
(
dict
!=
NULL
&&
merge_class_dict
(
dict
,
obj
)
==
0
)
assert
(
obj
);
result
=
PyDict_Keys
(
dict
);
assert
(
attrname
);
list
=
PyObject_GetAttrString
(
obj
,
attrname
);
Py_XDECREF
(
dict
);
if
(
list
==
NULL
)
return
result
;
PyErr_Clear
();
}
else
if
(
PyList_Check
(
list
))
{
/* Helper for PyObject_Dir of module objects: returns the module's __dict__. */
int
i
;
static
PyObject
*
for
(
i
=
0
;
i
<
PyList_GET_SIZE
(
list
);
++
i
)
{
_specialized_dir_module
(
PyObject
*
obj
)
PyObject
*
item
=
PyList_GET_ITEM
(
list
,
i
);
{
if
(
PyString_Check
(
item
))
{
PyObject
*
result
=
NULL
;
result
=
PyDict_SetItem
(
dict
,
item
,
Py_None
);
PyObject
*
dict
=
PyObject_GetAttrString
(
obj
,
"__dict__"
);
if
(
result
<
0
)
break
;
if
(
dict
!=
NULL
)
{
}
if
(
PyDict_Check
(
dict
))
result
=
PyDict_Keys
(
dict
);
else
{
PyErr_Format
(
PyExc_TypeError
,
"%.200s.__dict__ is not a dictionary"
,
PyModule_GetName
(
obj
));
}
}
}
}
Py_XDECREF
(
lis
t
);
Py_XDECREF
(
dic
t
);
return
result
;
return
result
;
}
}
/*
Like __builtin__.dir(arg). See bltinmodule.c's builtin_dir for the
/*
Helper for PyObject_Dir of generic objects: returns __dict__, __class__,
docstring, which should be kept in synch with this implementation. */
and recursively up the __class__.__bases__ chain.
*/
PyObject
*
static
PyObject
*
PyObject_Dir
(
PyObject
*
arg
)
_generic_dir
(
PyObject
*
obj
)
{
{
/* Set exactly one of these non-NULL before the end. */
PyObject
*
result
=
NULL
;
PyObject
*
result
=
NULL
;
/* result list */
PyObject
*
dict
=
NULL
;
PyObject
*
masterdict
=
NULL
;
/* result is masterdict.keys() */
PyObject
*
itsclass
=
NULL
;
/* If NULL arg, return the locals. */
/* Get __dict__ (which may or may not be a real dict...) */
if
(
arg
==
NULL
)
{
dict
=
PyObject_GetAttrString
(
obj
,
"__dict__"
);
PyObject
*
locals
=
PyEval_GetLocals
();
if
(
dict
==
NULL
)
{
if
(
locals
==
NULL
)
PyErr_Clear
();
goto
error
;
dict
=
PyDict_New
();
result
=
PyMapping_Keys
(
locals
);
if
(
result
==
NULL
)
goto
error
;
}
}
else
if
(
!
PyDict_Check
(
dict
))
{
/* Elif this is some form of module, we only want its dict. */
Py_DECREF
(
dict
);
else
if
(
PyModule_Check
(
arg
))
{
dict
=
PyDict_New
();
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
;
}
}
}
else
{
/* Elif some form of type or class, grab its dict and its bases.
/* Copy __dict__ to avoid mutating it. */
We deliberately don't suck up its __class__, as methods belonging
PyObject
*
temp
=
PyDict_Copy
(
dict
);
to the metaclass would probably be more confusing than helpful. */
Py_DECREF
(
dict
);
else
if
(
PyType_Check
(
arg
))
{
dict
=
temp
;
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. */
if
(
dict
==
NULL
)
goto
error
;
/* Merge in attrs reachable from its class. */
itsclass
=
PyObject_GetAttrString
(
obj
,
"__class__"
);
if
(
itsclass
==
NULL
)
/* XXX(tomer): Perhaps fall back to obj->ob_type if no
__class__ exists? */
PyErr_Clear
();
else
{
else
{
PyObject
*
itsclass
;
if
(
merge_class_dict
(
dict
,
itsclass
)
!=
0
)
/* Create a dict to start with. CAUTION: Not everything
responding to __dict__ returns a dict! */
masterdict
=
PyObject_GetAttrString
(
arg
,
"__dict__"
);
if
(
masterdict
==
NULL
)
{
PyErr_Clear
();
masterdict
=
PyDict_New
();
}
else
if
(
!
PyDict_Check
(
masterdict
))
{
Py_DECREF
(
masterdict
);
masterdict
=
PyDict_New
();
}
else
{
/* The object may have returned a reference to its
dict, so copy it to avoid mutating it. */
PyObject
*
temp
=
PyDict_Copy
(
masterdict
);
Py_DECREF
(
masterdict
);
masterdict
=
temp
;
}
if
(
masterdict
==
NULL
)
goto
error
;
goto
error
;
}
/* Merge in __members__ and __methods__ (if any).
result
=
PyDict_Keys
(
dict
);
XXX Would like this to go away someday; for now, it's
/* fall through */
XXX needed to get at im_self etc of method objects. */
error:
if
(
merge_list_attr
(
masterdict
,
arg
,
"__members__"
)
<
0
)
Py_XDECREF
(
itsclass
);
goto
error
;
Py_XDECREF
(
dict
)
;
if
(
merge_list_attr
(
masterdict
,
arg
,
"__methods__"
)
<
0
)
return
result
;
goto
error
;
}
/* Merge in attrs reachable from its class.
/* Helper for PyObject_Dir: object introspection.
CAUTION: Not all objects have a __class__ attr. */
This calls one of the above specialized versions if no __dir__ method
itsclass
=
PyObject_GetAttrString
(
arg
,
"__class__"
);
exists. */
if
(
itsclass
==
NULL
)
static
PyObject
*
PyErr_Clear
();
_dir_object
(
PyObject
*
obj
)
else
{
{
int
status
=
merge_class_dict
(
masterdict
,
itsclass
);
PyObject
*
result
=
NULL
;
Py_DECREF
(
itsclass
);
PyObject
*
dirfunc
=
PyObject_GetAttrString
((
PyObject
*
)
obj
->
ob_type
,
if
(
status
<
0
)
"__dir__"
);
goto
error
;
}
}
assert
((
result
==
NULL
)
^
(
masterdict
==
NULL
));
assert
(
obj
);
if
(
masterdict
!=
NULL
)
{
if
(
dirfunc
==
NULL
)
{
/* The result comes from its keys. */
/* use default implementation */
assert
(
result
==
NULL
);
PyErr_Clear
();
result
=
PyDict_Keys
(
masterdict
);
if
(
PyModule_Check
(
obj
))
if
(
result
==
NULL
)
result
=
_specialized_dir_module
(
obj
);
goto
error
;
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
)
return
NULL
;
assert
(
result
);
/* result must be a list */
if
(
!
PyList_Check
(
result
))
{
/* XXX(gbrandl): could also check if all items are strings */
PyErr_Format
(
PyExc_TypeError
,
if
(
!
PyList_Check
(
result
))
{
"Expected keys() to be a list, not '%.200s'"
,
PyErr_Format
(
PyExc_TypeError
,
result
->
ob_type
->
tp_name
);
"__dir__() must return a list, not %.200s"
,
goto
error
;
result
->
ob_type
->
tp_name
);
Py_DECREF
(
result
);
result
=
NULL
;
}
}
}
if
(
PyList_Sort
(
result
)
!=
0
)
goto
error
;
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
else
goto
normal_return
;
/* object -- introspect the object */
result
=
_dir_object
(
obj
);
error:
assert
(
result
==
NULL
||
PyList_Check
(
result
));
Py_XDECREF
(
result
);
result
=
NULL
;
if
(
result
!=
NULL
&&
PyList_Sort
(
result
)
!=
0
)
{
/* fall through */
/* sorting the list failed */
normal_return:
Py_DECREF
(
result
);
Py_XDECREF
(
masterdict
);
result
=
NULL
;
}
return
result
;
return
result
;
}
}
...
...
Python/bltinmodule.c
View file @
e32b4224
...
@@ -427,15 +427,16 @@ builtin_dir(PyObject *self, PyObject *args)
...
@@ -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
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment