mirror of
https://github.com/python/cpython.git
synced 2025-07-29 06:05:00 +00:00
Backport from Py3k branch:
Patch #1591665: implement the __dir__() special function lookup in PyObject_Dir. Had to change a few bits of the patch because classobjs and __methods__ are still in Py2.6.
This commit is contained in:
parent
2681beb23e
commit
871f1bc601
5 changed files with 292 additions and 140 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__()}
|
||||||
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:
|
||||||
['__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}
|
||||||
|
|
|
@ -259,12 +259,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))
|
||||||
|
|
|
@ -12,6 +12,10 @@ What's New in Python 2.6 alpha 1?
|
||||||
Core and builtins
|
Core and builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- 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__().
|
||||||
|
|
||||||
- Patch #1675981: remove unreachable code from ``type.__new__()`` method.
|
- Patch #1675981: remove unreachable code from ``type.__new__()`` method.
|
||||||
|
|
||||||
- Patch #1491866: change the complex() constructor to allow parthensized
|
- Patch #1491866: change the complex() constructor to allow parthensized
|
||||||
|
|
287
Objects/object.c
287
Objects/object.c
|
@ -1566,6 +1566,8 @@ PyCallable_Check(PyObject *x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------- 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
|
||||||
|
@ -1662,121 +1664,192 @@ merge_list_attr(PyObject* dict, PyObject* obj, const char *attrname)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like __builtin__.dir(arg). See bltinmodule.c's builtin_dir for the
|
/* Helper for PyObject_Dir without arguments: returns the local scope. */
|
||||||
docstring, which should be kept in synch with this implementation. */
|
static PyObject *
|
||||||
|
_dir_locals()
|
||||||
PyObject *
|
|
||||||
PyObject_Dir(PyObject *arg)
|
|
||||||
{
|
{
|
||||||
/* Set exactly one of these non-NULL before the end. */
|
PyObject *names;
|
||||||
PyObject *result = NULL; /* result list */
|
PyObject *locals = PyEval_GetLocals();
|
||||||
PyObject *masterdict = NULL; /* result is masterdict.keys() */
|
|
||||||
|
|
||||||
/* If NULL arg, return the locals. */
|
if (locals == NULL) {
|
||||||
if (arg == NULL) {
|
PyErr_SetString(PyExc_SystemError, "frame does not exist");
|
||||||
PyObject *locals = PyEval_GetLocals();
|
return NULL;
|
||||||
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. */
|
names = PyMapping_Keys(locals);
|
||||||
else if (PyModule_Check(arg)) {
|
if (!names)
|
||||||
masterdict = PyObject_GetAttrString(arg, "__dict__");
|
return NULL;
|
||||||
if (masterdict == NULL)
|
if (!PyList_Check(names)) {
|
||||||
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) || PyClass_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. 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;
|
|
||||||
|
|
||||||
/* Merge in __members__ and __methods__ (if any).
|
|
||||||
XXX Would like this to go away someday; for now, it's
|
|
||||||
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)
|
|
||||||
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 = PyDict_Keys(masterdict);
|
|
||||||
if (result == NULL)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(result);
|
|
||||||
if (!PyList_Check(result)) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"Expected keys() to be a list, not '%.200s'",
|
"dir(): expected keys() of locals to be a list, "
|
||||||
result->ob_type->tp_name);
|
"not '%.200s'", names->ob_type->tp_name);
|
||||||
goto error;
|
Py_DECREF(names);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
if (PyList_Sort(result) != 0)
|
/* the locals don't need to be DECREF'd */
|
||||||
goto error;
|
return names;
|
||||||
else
|
}
|
||||||
goto normal_return;
|
|
||||||
|
|
||||||
error:
|
/* Helper for PyObject_Dir of type objects: returns __dict__ and __bases__.
|
||||||
Py_XDECREF(result);
|
We deliberately don't suck up its __class__, as methods belonging to the
|
||||||
result = NULL;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper for PyObject_Dir of module objects: returns the module's __dict__. */
|
||||||
|
static PyObject *
|
||||||
|
_specialized_dir_module(PyObject *obj)
|
||||||
|
{
|
||||||
|
PyObject *result = NULL;
|
||||||
|
PyObject *dict = PyObject_GetAttrString(obj, "__dict__");
|
||||||
|
|
||||||
|
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(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();
|
||||||
|
dict = PyDict_New();
|
||||||
|
}
|
||||||
|
else if (!PyDict_Check(dict)) {
|
||||||
|
Py_DECREF(dict);
|
||||||
|
dict = PyDict_New();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Copy __dict__ to avoid mutating it. */
|
||||||
|
PyObject *temp = PyDict_Copy(dict);
|
||||||
|
Py_DECREF(dict);
|
||||||
|
dict = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Merge in __members__ and __methods__ (if any).
|
||||||
|
* This is removed in Python 3000. */
|
||||||
|
if (merge_list_attr(dict, obj, "__members__") < 0)
|
||||||
|
goto error;
|
||||||
|
if (merge_list_attr(dict, obj, "__methods__") < 0)
|
||||||
|
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 {
|
||||||
|
if (merge_class_dict(dict, itsclass) != 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = PyDict_Keys(dict);
|
||||||
/* fall through */
|
/* fall through */
|
||||||
normal_return:
|
error:
|
||||||
Py_XDECREF(masterdict);
|
Py_XDECREF(itsclass);
|
||||||
|
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) || PyClass_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;
|
||||||
|
|
||||||
|
/* result must be a list */
|
||||||
|
/* XXX(gbrandl): could also check if all items are strings */
|
||||||
|
if (!PyList_Check(result)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"__dir__() must return a list, not %.200s",
|
||||||
|
result->ob_type->tp_name);
|
||||||
|
Py_DECREF(result);
|
||||||
|
result = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -495,15 +495,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