_PyMethodDescr_FastCallKeywords() is an optimization in ceval.c to call methods. Problem: it introduces a wrong bug.
>>> import io
>>> help(io.FileIO.write)
Help on method_descriptor:
write(self, b, /)
Write buffer b to file, return number of bytes written.
>>> f=io.FileIO("/dev/null", "wb")
>>> io.FileIO.write(f, b"data") # ok
4
>>> io.FileIO.write(self=f, b=b"data") # ???
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor 'write' of '_io.FileIO' object needs an argument
io.FileIO.write is a "method_descriptor" builtin object. It's called from ceval.c call_function() using _PyMethodDescr_FastCallKeywords() which starts with:
/* Make sure that the first argument is acceptable as 'self' */
if (nargs < 1) {
PyErr_Format(PyExc_TypeError,
"descriptor '%V' of '%.100s' "
"object needs an argument",
descr_name((PyDescrObject *)descr), "?",
PyDescr_TYPE(descr)->tp_name);
return NULL;
}
self = args[0];
...
The bug is not a regression caused by the optimization. It exists in Python 3.6 which doesn't have the optimization:
$ python3.6
Python 3.6.8+ (heads/3.6:b241af861b, Mar 11 2019, 08:55:59)
>>> import io
>>> f=io.FileIO("/dev/null", "wb")
>>> io.FileIO.write(f, b"data") # ok
4
>>> io.FileIO.write(self=f, b=b"data") # ???
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor 'write' of '_io.FileIO' object needs an argument
Extract of Python 3.6 methoddescr_call():
/* Make sure that the first argument is acceptable as 'self' */
assert(PyTuple_Check(args));
argc = PyTuple_GET_SIZE(args);
if (argc < 1) {
PyErr_Format(PyExc_TypeError,
"descriptor '%V' of '%.100s' "
"object needs an argument",
descr_name((PyDescrObject *)descr), "?",
PyDescr_TYPE(descr)->tp_name);
return NULL;
}
self = PyTuple_GET_ITEM(args, 0);
Python 2.7 raises the same exception, but the docstring is different:
$ python2
Python 2.7.15 (default, Oct 15 2018, 15:26:09)
>>> import io
>>> f=io.FileIO("/dev/null", "wb")
>>> io.FileIO.write(f, b"data")
4L
>>> io.FileIO.write(self=f, b=b"data")
TypeError: descriptor 'write' of '_io.FileIO' object needs an argument
>>> help(io.FileIO.write)
write(...)
write(b) -> int. Write array of bytes b, return number written.
--
At this point, it's unclear to me if it's a bug... of a feature :-)
It seems like self is a positional-only argument, but the error message isn't helpful.
|