This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author vstinner
Recipients pablogsal, vstinner
Date 2019-03-26.13:13:00
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1553605980.78.0.197035304135.issue36437@roundup.psfhosted.org>
In-reply-to
Content
_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.
History
Date User Action Args
2019-03-26 13:13:00vstinnersetrecipients: + vstinner, pablogsal
2019-03-26 13:13:00vstinnersetmessageid: <1553605980.78.0.197035304135.issue36437@roundup.psfhosted.org>
2019-03-26 13:13:00vstinnerlinkissue36437 messages
2019-03-26 13:13:00vstinnercreate