diff --git a/Include/eval.h b/Include/eval.h index a1c6e81..611716b 100644 --- a/Include/eval.h +++ b/Include/eval.h @@ -15,7 +15,7 @@ PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(PyObject *co, PyObject **args, int argc, PyObject **kwds, int kwdc, PyObject **defs, int defc, - PyObject *kwdefs, PyObject *closure); + PyObject *kwdefs, PyObject *closure, PyObject *qualname); #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args); diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 6b6c12d..fcd82f5 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -295,6 +295,42 @@ Too many arguments: Traceback (most recent call last): ... TypeError: f() takes from 1 to 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given + >>> class A: + ... def __init__(): pass + >>> A() + Traceback (most recent call last): + ... + TypeError: A.__init__() takes 0 positional arguments but 1 was given + >>> class A: + ... def __init__(a): pass + >>> A(1) + Traceback (most recent call last): + ... + TypeError: A.__init__() takes 1 positional argument but 2 were given + >>> class A: + ... def __init__(a, b=1): pass + >>> A(1, 2) + Traceback (most recent call last): + ... + TypeError: A.__init__() takes from 1 to 2 positional arguments but 3 were given + >>> class A: + ... def __init__(*, kw): pass + >>> A(kw=3) + Traceback (most recent call last): + ... + TypeError: A.__init__() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given + >>> class A: + ... def __init__(*, kw, b): pass + >>> A(1, 2, b=3, kw=3) + Traceback (most recent call last): + ... + TypeError: A.__init__() takes 0 positional arguments but 3 positional arguments (and 2 keyword-only arguments) were given + >>> class A: + ... def __init__(a, b=2, *, kw): pass + >>> A(2, 3, kw=4) + Traceback (most recent call last): + ... + TypeError: A.__init__() takes from 1 to 2 positional arguments but 3 positional arguments (and 1 keyword-only argument) were given Too few and missing arguments: @@ -323,6 +359,41 @@ Too few and missing arguments: Traceback (most recent call last): ... TypeError: f() missing 1 required positional argument: 'a' + >>> class A: + ... @staticmethod + ... def f(a): pass + >>> A.f() + Traceback (most recent call last): + ... + TypeError: A.f() missing 1 required positional argument: 'a' + >>> class A: + ... @staticmethod + ... def f(a, b): pass + >>> A.f() + Traceback (most recent call last): + ... + TypeError: A.f() missing 2 required positional arguments: 'a' and 'b' + >>> class A: + ... @staticmethod + ... def f(a, b, c): pass + >>> A.f() + Traceback (most recent call last): + ... + TypeError: A.f() missing 3 required positional arguments: 'a', 'b', and 'c' + >>> class A: + ... @staticmethod + ... def f(a, b, c, d, e): pass + >>> A.f() + Traceback (most recent call last): + ... + TypeError: A.f() missing 5 required positional arguments: 'a', 'b', 'c', 'd', and 'e' + >>> class A: + ... @staticmethod + ... def f(a, b=4, c=5, d=5): pass + >>> A.f(c=12, b=9) + Traceback (most recent call last): + ... + TypeError: A.f() missing 1 required positional argument: 'a' Same with keyword only args: @@ -336,7 +407,37 @@ Same with keyword only args: Traceback (most recent call last): ... TypeError: f() missing 5 required keyword-only arguments: 'a', 'b', 'c', 'd', and 'e' + >>> class A: + ... @staticmethod + ... def f(*, w): pass + >>> A.f() + Traceback (most recent call last): + ... + TypeError: A.f() missing 1 required keyword-only argument: 'w' + >>> class A: + ... @staticmethod + ... def f(*, a, b, c, d, e): pass + >>> A.f() + Traceback (most recent call last): + ... + TypeError: A.f() missing 5 required keyword-only arguments: 'a', 'b', 'c', 'd', and 'e' + +Test errors in inner functions: + >>> def f(): + ... def g(a): pass + ... g() + >>> f() + Traceback (most recent call last): + ... + TypeError: f..g() missing 1 required positional argument: 'a' + >>> def f(): + ... def g(): pass + ... g(1) + >>> f() + Traceback (most recent call last): + ... + TypeError: f..g() takes 0 positional arguments but 1 was given """ import sys diff --git a/Lib/test/test_keywordonlyarg.py b/Lib/test/test_keywordonlyarg.py index 7f315d4..aaaa348 100644 --- a/Lib/test/test_keywordonlyarg.py +++ b/Lib/test/test_keywordonlyarg.py @@ -76,7 +76,9 @@ class KeywordOnlyArgTestCase(unittest.TestCase): pass with self.assertRaises(TypeError) as exc: f(1, 2, 3) - expected = "f() takes from 1 to 2 positional arguments but 3 were given" + expected = ("KeywordOnlyArgTestCase.testTooManyPositionalErrorMessage." + ".f() takes from 1 to 2 positional arguments but 3 were " + "given") self.assertEqual(str(exc.exception), expected) def testSyntaxErrorForFunctionCall(self): diff --git a/Objects/funcobject.c b/Objects/funcobject.c index b043934..878ca86 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -635,7 +635,8 @@ function_call(PyObject *func, PyObject *arg, PyObject *kw) &PyTuple_GET_ITEM(arg, 0), PyTuple_GET_SIZE(arg), k, nk, d, nd, PyFunction_GET_KW_DEFAULTS(func), - PyFunction_GET_CLOSURE(func)); + PyFunction_GET_CLOSURE(func), + ((PyFunctionObject *)func)->func_qualname); Py_XDECREF(kwtuple); diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index e733485..fce6140 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -175,7 +175,8 @@ builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) } cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns, NULL, 0, NULL, 0, NULL, 0, NULL, - PyFunction_GET_CLOSURE(func)); + PyFunction_GET_CLOSURE(func), + ((PyFunctionObject *)func)->func_qualname); if (cell != NULL) { PyObject *margs; margs = PyTuple_Pack(3, name, bases, ns); diff --git a/Python/ceval.c b/Python/ceval.c index d68cdc6..6d7dffa 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -777,7 +777,7 @@ PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals) (PyObject **)NULL, 0, (PyObject **)NULL, 0, (PyObject **)NULL, 0, - NULL, NULL); + NULL, NULL, NULL); } @@ -3257,7 +3257,8 @@ exit_eval_frame: } static void -format_missing(const char *kind, PyCodeObject *co, PyObject *names) +format_missing(const char *kind, PyCodeObject *co, PyObject *names, + PyObject *qualname) { int err; Py_ssize_t len = PyList_GET_SIZE(names); @@ -3310,7 +3311,7 @@ format_missing(const char *kind, PyCodeObject *co, PyObject *names) return; PyErr_Format(PyExc_TypeError, "%U() missing %i required %s argument%s: %U", - co->co_name, + qualname ? qualname : co->co_name, len, kind, len == 1 ? "" : "s", @@ -3320,7 +3321,7 @@ format_missing(const char *kind, PyCodeObject *co, PyObject *names) static void missing_arguments(PyCodeObject *co, int missing, int defcount, - PyObject **fastlocals) + PyObject **fastlocals, PyObject *qualname) { int i, j = 0; int start, end; @@ -3352,12 +3353,13 @@ missing_arguments(PyCodeObject *co, int missing, int defcount, } } assert(j == missing); - format_missing(kind, co, missing_names); + format_missing(kind, co, missing_names, qualname); Py_DECREF(missing_names); } static void -too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlocals) +too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlocals, + PyObject *qualname) { int plural; int kwonly_given = 0; @@ -3396,7 +3398,7 @@ too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlo } PyErr_Format(PyExc_TypeError, "%U() takes %U positional argument%s but %d%U %s given", - co->co_name, + qualname ? qualname : co->co_name, sig, plural ? "s" : "", given, @@ -3478,7 +3480,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (keyword == NULL || !PyUnicode_Check(keyword)) { PyErr_Format(PyExc_TypeError, "%U() keywords must be strings", - co->co_name); + qualname ? qualname : co->co_name); goto fail; } /* Speed hack: do raw pointer compares. As names are @@ -3503,7 +3505,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyErr_Format(PyExc_TypeError, "%U() got an unexpected " "keyword argument '%S'", - co->co_name, + qualname ? qualname : co->co_name, keyword); goto fail; } @@ -3516,7 +3518,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyErr_Format(PyExc_TypeError, "%U() got multiple " "values for argument '%S'", - co->co_name, + qualname ? qualname : co->co_name, keyword); goto fail; } @@ -3524,7 +3526,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, SETLOCAL(j, value); } if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { - too_many_positional(co, argcount, defcount, fastlocals); + too_many_positional(co, argcount, defcount, fastlocals, qualname); goto fail; } if (argcount < co->co_argcount) { @@ -3534,7 +3536,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (GETLOCAL(i) == NULL) missing++; if (missing) { - missing_arguments(co, missing, defcount, fastlocals); + missing_arguments(co, missing, defcount, fastlocals, qualname); goto fail; } if (n > m) @@ -3567,7 +3569,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, missing++; } if (missing) { - missing_arguments(co, missing, -1, fastlocals); + missing_arguments(co, missing, -1, fastlocals, qualname); goto fail; } } @@ -3628,12 +3630,13 @@ fail: /* Jump here from prelude on failure */ PyObject * PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, + PyObject *qualname) { return _PyEval_EvalCodeWithName(_co, globals, locals, args, argcount, kws, kwcount, defs, defcount, kwdefs, closure, - NULL, NULL); + NULL, qualname); } static PyObject *