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.

classification
Title: method_descriptor surprising error message when self is passed a keyword argument
Type: Stage: resolved
Components: Versions: Python 3.8, Python 3.7
process
Status: closed Resolution: out of date
Dependencies: Superseder:
Assigned To: Nosy List: pablogsal, vstinner
Priority: normal Keywords:

Created on 2019-03-26 13:13 by vstinner, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (1)
msg338886 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-03-26 13:13
_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
2022-04-11 14:59:13adminsetgithub: 80618
2021-09-21 22:24:59vstinnersetstatus: open -> closed
resolution: out of date
stage: resolved
2019-03-26 13:13:00vstinnercreate