From 0be9cd1ba34995a7a03f8b77a63a752da08b6dee Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 22 Aug 2008 20:15:16 +0200 Subject: [PATCH] more accurate exception messages when passing a wrong argument count (adapted to Py3) --- Cython/Compiler/Nodes.py | 57 ++++++++++++++++++----------------- tests/run/classkwonlyargs.pyx | 18 +++-------- tests/run/extkwonlyargs.pyx | 8 ++--- tests/run/extstarargs.pyx | 18 +++++------ tests/run/kwonlyargs.pyx | 21 ++++++++++--- tests/run/kwonlyargscall.pyx | 12 +++----- tests/run/simpcall.pyx | 12 +++----- tests/run/starargs.pyx | 20 ++++++------ 8 files changed, 83 insertions(+), 83 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 729b4e6f..bf55c739 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1683,7 +1683,7 @@ class DefNode(FuncDefNode): def generate_stararg_copy_code(self, code): if not self.star_arg: - self.generate_positional_args_check(0, code) + self.generate_positional_args_check(0, True, code) code.globalstate.use_utility_code(keyword_string_check_utility_code) @@ -1760,7 +1760,9 @@ class DefNode(FuncDefNode): max_args = max_positional_args + len(kw_only_args) if not self.star_arg: - self.generate_positional_args_check(max_positional_args, code) + has_fixed_positional_count = min_positional_args == max_positional_args + self.generate_positional_args_check( + max_positional_args, has_fixed_positional_count, code) if self.star_arg or self.starstar_arg: self.generate_stararg_getting_code(max_positional_args, code) @@ -1834,10 +1836,11 @@ class DefNode(FuncDefNode): code.putln('}') else: # check if we have all required positional arguments + exact_count = not self.star_arg and min_positional_args == max_positional_args code.putln('} else if (unlikely(PyTuple_GET_SIZE(%s) < %d)) {' % ( Naming.args_cname, min_positional_args)) - code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, PyTuple_GET_SIZE(%s)); ' % ( - self.name.utf8encode(), min_positional_args, + code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, PyTuple_GET_SIZE(%s)); ' % ( + self.name.utf8encode(), exact_count, min_positional_args, max_positional_args, Naming.args_cname)) code.putln(code.error_goto(self.pos)) @@ -1856,13 +1859,15 @@ class DefNode(FuncDefNode): code.putln('}') return - def generate_positional_args_check(self, max_positional_args, code): + def generate_positional_args_check(self, max_positional_args, + has_fixed_pos_count, code): # make sure supernumerous positional arguments do not run # into keyword-only arguments and provide a helpful message code.putln("if (unlikely(PyTuple_GET_SIZE(%s) > %d)) {" % ( Naming.args_cname, max_positional_args)) - code.putln('__Pyx_RaiseArgtupleInvalid("%s", 0, %d, PyTuple_GET_SIZE(%s));' % ( - self.name.utf8encode(), max_positional_args, Naming.args_cname)) + code.putln('__Pyx_RaiseArgtupleInvalid("%s", %d, 0, %d, PyTuple_GET_SIZE(%s));' % ( + self.name.utf8encode(), has_fixed_pos_count, + max_positional_args, Naming.args_cname)) code.putln("return %s;" % self.error_value()) code.putln("}") @@ -4303,41 +4308,39 @@ static INLINE int __Pyx_SplitStarArg( raise_argtuple_invalid_utility_code = [ """ -static INLINE void __Pyx_RaiseArgtupleInvalid(char* func_name, +static void __Pyx_RaiseArgtupleInvalid(char* func_name, int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/ """,""" -static INLINE void __Pyx_RaiseArgtupleInvalid( +static void __Pyx_RaiseArgtupleInvalid( char* func_name, + int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found) { Py_ssize_t num_expected; - char* message; + char *message, *number, *more_or_less; - if (num_found < num_min) { - num_expected = num_min; - message = + message = #if PY_VERSION_HEX < 0x02050000 - "%s() takes at least %d positional arguments (%d given)"; - #elif PY_MAJOR_VERSION >= 3 - "%U() takes at most %zd positional arguments (%zd given)"; + "%s() takes %s %d positional argument%s (%d given)"; #else - "%s() takes at least %zd positional arguments (%zd given)"; + "%s() takes %s %zd positional argument%s (%zd given)"; #endif + + if (num_found < num_min) { + num_expected = num_min; + more_or_less = "at least"; } else { num_expected = num_max; - message = - #if PY_VERSION_HEX < 0x02050000 - "%s() takes at most %d positional arguments (%d given)"; - #elif PY_MAJOR_VERSION >= 3 - "%U() takes at most %zd positional arguments (%zd given)"; - #else - "%s() takes at most %zd positional arguments (%zd given)"; - #endif + more_or_less = "at most"; + } + if (exact) { + more_or_less = "exactly"; } - PyErr_Format(PyExc_TypeError, message, func_name, - num_expected, num_found); + number = (num_expected == 1) ? "" : "s"; + PyErr_Format(PyExc_TypeError, message, func_name, more_or_less, + num_expected, number, num_found); } """] diff --git a/tests/run/classkwonlyargs.pyx b/tests/run/classkwonlyargs.pyx index 5ab9a2da..eac6c5c0 100644 --- a/tests/run/classkwonlyargs.pyx +++ b/tests/run/classkwonlyargs.pyx @@ -5,7 +5,7 @@ __doc__ = u""" >>> b(1,2,3) >>> b(1,2,3,4) Traceback (most recent call last): - TypeError: b() takes at most 4 positional arguments (5 given) + TypeError: b() takes exactly 4 positional arguments (5 given) >>> c(1,2) >>> c(1,2,3) @@ -18,7 +18,7 @@ __doc__ = u""" >>> d(1,2,3) Traceback (most recent call last): - TypeError: d() takes at most 3 positional arguments (4 given) + TypeError: d() takes exactly 3 positional arguments (4 given) >>> d(1,2, d=1) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -37,7 +37,7 @@ __doc__ = u""" >>> f(1,2,3) Traceback (most recent call last): - TypeError: f() takes at most 3 positional arguments (4 given) + TypeError: f() takes exactly 3 positional arguments (4 given) >>> f(1,2) Traceback (most recent call last): TypeError: f() needs keyword-only argument c @@ -49,9 +49,9 @@ __doc__ = u""" >>> g(1,2, c=1, e=0, f=2, d=11) >>> g(1,2, c=1, f=2, e=0, x=25) - >>> g(1,2,3) #doctest: +ELLIPSIS + >>> g(1,2,3) Traceback (most recent call last): - TypeError: g() takes at most 3 positional arguments (4 given) + TypeError: g() takes exactly 3 positional arguments (4 given) >>> g(1,2) Traceback (most recent call last): TypeError: g() needs keyword-only argument c @@ -84,14 +84,6 @@ __doc__ = u""" TypeError: k() needs keyword-only argument f """ -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"Error: (.*)exactly(.*)", u"Error: \\1at most\\2", __doc__) - -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"(ELLIPSIS[^>]*Error: )[^\n]*\n", u"\\1...\n", __doc__, re.M) - class Spam: def b(self, a, b, c): pass diff --git a/tests/run/extkwonlyargs.pyx b/tests/run/extkwonlyargs.pyx index b819419c..c3371e03 100644 --- a/tests/run/extkwonlyargs.pyx +++ b/tests/run/extkwonlyargs.pyx @@ -5,7 +5,7 @@ __doc__ = u""" >>> b(1,2,3) >>> b(1,2,3,4) Traceback (most recent call last): - TypeError: b() takes at most 3 positional arguments (4 given) + TypeError: b() takes exactly 3 positional arguments (4 given) >>> c(1,2) >>> c(1,2,3) @@ -18,7 +18,7 @@ __doc__ = u""" >>> d(1,2,3) Traceback (most recent call last): - TypeError: d() takes at most 2 positional arguments (3 given) + TypeError: d() takes exactly 2 positional arguments (3 given) >>> d(1,2, d=1) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -37,7 +37,7 @@ __doc__ = u""" >>> f(1,2,3) Traceback (most recent call last): - TypeError: f() takes at most 2 positional arguments (3 given) + TypeError: f() takes exactly 2 positional arguments (3 given) >>> f(1,2) Traceback (most recent call last): TypeError: f() needs keyword-only argument c @@ -51,7 +51,7 @@ __doc__ = u""" >>> g(1,2,3) Traceback (most recent call last): - TypeError: g() takes at most 2 positional arguments (3 given) + TypeError: g() takes exactly 2 positional arguments (3 given) >>> g(1,2) Traceback (most recent call last): TypeError: g() needs keyword-only argument c diff --git a/tests/run/extstarargs.pyx b/tests/run/extstarargs.pyx index 0b785999..20708bd5 100644 --- a/tests/run/extstarargs.pyx +++ b/tests/run/extstarargs.pyx @@ -5,12 +5,12 @@ __doc__ = u""" >>> spam(1,2,3) (1, 2, 3) - >>> spam(1,2) #doctest: +ELLIPSIS + >>> spam(1,2) Traceback (most recent call last): - TypeError: spam() takes at least 3 positional arguments (2 given) + TypeError: spam() takes exactly 3 positional arguments (2 given) >>> spam(1,2,3,4) Traceback (most recent call last): - TypeError: spam() takes at most 3 positional arguments (4 given) + TypeError: spam() takes exactly 3 positional arguments (4 given) >>> spam(1,2,3, a=1) #doctest: +ELLIPSIS Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -21,9 +21,9 @@ __doc__ = u""" (1, 2, 3, (4,)) >>> grail(1,2,3,4,5,6,7,8,9) (1, 2, 3, (4, 5, 6, 7, 8, 9)) - >>> grail(1,2) #doctest: +ELLIPSIS + >>> grail(1,2) Traceback (most recent call last): - TypeError: grail() takes exactly 3 arguments (2 given) + TypeError: grail() takes at least 3 positional arguments (2 given) >>> grail(1,2,3, a=1) #doctest: +ELLIPSIS Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -32,7 +32,7 @@ __doc__ = u""" (1, 2, 3, ()) >>> swallow(1,2,3,4) Traceback (most recent call last): - TypeError: swallow() takes at most 3 positional arguments (4 given) + TypeError: swallow() takes exactly 3 positional arguments (4 given) >>> swallow(1,2,3, a=1, b=2) (1, 2, 3, (('a', 1), ('b', 2))) >>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS @@ -68,13 +68,13 @@ __doc__ = u""" (('a', 1), ('b', 2)) >>> onlyk(1) Traceback (most recent call last): - TypeError: onlyk() takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes exactly 0 positional arguments (1 given) >>> onlyk(1, 2) Traceback (most recent call last): - TypeError: onlyk() takes at most 0 positional arguments (2 given) + TypeError: onlyk() takes exactly 0 positional arguments (2 given) >>> onlyk(1, a=1, b=2) Traceback (most recent call last): - TypeError: onlyk() takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes exactly 0 positional arguments (1 given) >>> tk(a=1) (('a', 1),) diff --git a/tests/run/kwonlyargs.pyx b/tests/run/kwonlyargs.pyx index b60ac4d5..e89caf17 100644 --- a/tests/run/kwonlyargs.pyx +++ b/tests/run/kwonlyargs.pyx @@ -2,7 +2,7 @@ __doc__ = u""" >>> b(1,2,3) >>> b(1,2,3,4) Traceback (most recent call last): - TypeError: b() takes at most 3 positional arguments (4 given) + TypeError: b() takes exactly 3 positional arguments (4 given) >>> c(1,2) >>> c(1,2,3) @@ -15,7 +15,7 @@ __doc__ = u""" >>> d(1,2,3) Traceback (most recent call last): - TypeError: d() takes at most 2 positional arguments (3 given) + TypeError: d() takes exactly 2 positional arguments (3 given) >>> d(1,2, d=1) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -34,7 +34,7 @@ __doc__ = u""" >>> f(1,2,3) Traceback (most recent call last): - TypeError: f() takes at most 2 positional arguments (3 given) + TypeError: f() takes exactly 2 positional arguments (3 given) >>> f(1,2) Traceback (most recent call last): TypeError: f() needs keyword-only argument c @@ -48,7 +48,7 @@ __doc__ = u""" >>> g(1,2,3) Traceback (most recent call last): - TypeError: g() takes at most 2 positional arguments (3 given) + TypeError: g() takes exactly 2 positional arguments (3 given) >>> g(1,2) Traceback (most recent call last): TypeError: g() needs keyword-only argument c @@ -79,6 +79,16 @@ __doc__ = u""" >>> k(1,2, d=1) Traceback (most recent call last): TypeError: k() needs keyword-only argument f + + >>> l(a=1, b=2) + >>> l(a=1, b=2, c=1) + + >>> l(1,2,3) + Traceback (most recent call last): + TypeError: l() takes exactly 0 positional arguments (3 given) + >>> l(1,2, d=1) + Traceback (most recent call last): + TypeError: l() takes exactly 0 positional arguments (2 given) """ def b(a, b, c): @@ -104,3 +114,6 @@ def h(a, b, *args, c, d = 42, e = 17, f, **kwds): def k(a, b, c=1, *args, d = 42, e = 17, f, **kwds): pass + +def l(*, a, b, c = 88): + pass diff --git a/tests/run/kwonlyargscall.pyx b/tests/run/kwonlyargscall.pyx index 296e512e..910a5c04 100644 --- a/tests/run/kwonlyargscall.pyx +++ b/tests/run/kwonlyargscall.pyx @@ -3,7 +3,7 @@ __doc__ = u""" 1 2 3 >>> call4(b) Traceback (most recent call last): - TypeError: b() takes at most 3 positional arguments (4 given) + TypeError: b() takes exactly 3 positional arguments (4 given) >>> call2(c) 1 2 1 @@ -20,7 +20,7 @@ __doc__ = u""" >>> call3(d) Traceback (most recent call last): - TypeError: d() takes at most 2 positional arguments (3 given) + TypeError: d() takes exactly 2 positional arguments (3 given) >>> call2d(d) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -46,7 +46,7 @@ __doc__ = u""" >>> call3(f) Traceback (most recent call last): - TypeError: f() takes at most 2 positional arguments (3 given) + TypeError: f() takes exactly 2 positional arguments (3 given) >>> call2(f) Traceback (most recent call last): TypeError: f() needs keyword-only argument c @@ -63,7 +63,7 @@ __doc__ = u""" >>> call3(g) Traceback (most recent call last): - TypeError: g() takes at most 2 positional arguments (3 given) + TypeError: g() takes exactly 2 positional arguments (3 given) >>> call2(g) Traceback (most recent call last): TypeError: g() needs keyword-only argument c @@ -104,10 +104,6 @@ __doc__ = u""" TypeError: k() needs keyword-only argument f """ -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"Error: (.*)exactly(.*)", u"Error: \\1at most\\2", __doc__) - # the calls: def call2(f): diff --git a/tests/run/simpcall.pyx b/tests/run/simpcall.pyx index e8f788ae..497df8b0 100644 --- a/tests/run/simpcall.pyx +++ b/tests/run/simpcall.pyx @@ -4,19 +4,15 @@ __doc__ = u""" Traceback (most recent call last): TypeError: an integer is required - >>> fail0(1,2) #doctest: +ELLIPSIS + >>> fail0(1,2) Traceback (most recent call last): - TypeError: function takes exactly 2 arguments (0 given) + TypeError: f() takes exactly 2 positional arguments (0 given) - >>> fail1(1,2) #doctest: +ELLIPSIS + >>> fail1(1,2) Traceback (most recent call last): - TypeError: function takes exactly 2 arguments (1 given) + TypeError: f() takes exactly 2 positional arguments (1 given) """ -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"Error: .*exactly.*", u"Error: ...", __doc__) - import sys if sys.version_info[0] < 3: __doc__ = __doc__.replace(u" b'", u" '") diff --git a/tests/run/starargs.pyx b/tests/run/starargs.pyx index d16a2d97..b6910eea 100644 --- a/tests/run/starargs.pyx +++ b/tests/run/starargs.pyx @@ -1,13 +1,13 @@ __doc__ = u""" >>> spam(1,2,3) (1, 2, 3) - >>> spam(1,2) #doctest: +ELLIPSIS + >>> spam(1,2) Traceback (most recent call last): - TypeError: spam() takes at least 3 positional arguments (2 given) + TypeError: spam() takes exactly 3 positional arguments (2 given) >>> spam(1,2,3,4) Traceback (most recent call last): - TypeError: spam() takes at most 3 positional arguments (4 given) - >>> spam(1,2,3, a=1) #doctest: +ELLIPSIS + TypeError: spam() takes exactly 3 positional arguments (4 given) + >>> spam(1,2,3, a=1) Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -17,10 +17,10 @@ __doc__ = u""" (1, 2, 3, (4,)) >>> grail(1,2,3,4,5,6,7,8,9) (1, 2, 3, (4, 5, 6, 7, 8, 9)) - >>> grail(1,2) #doctest: +ELLIPSIS + >>> grail(1,2) Traceback (most recent call last): TypeError: grail() takes at least 3 positional arguments (2 given) - >>> grail(1,2,3, a=1) #doctest: +ELLIPSIS + >>> grail(1,2,3, a=1) Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -28,7 +28,7 @@ __doc__ = u""" (1, 2, 3, ()) >>> swallow(1,2,3,4) Traceback (most recent call last): - TypeError: swallow() takes at most 3 positional arguments (4 given) + TypeError: swallow() takes exactly 3 positional arguments (4 given) >>> swallow(1,2,3, a=1, b=2) (1, 2, 3, (('a', 1), ('b', 2))) >>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS @@ -64,13 +64,13 @@ __doc__ = u""" (('a', 1), ('b', 2)) >>> onlyk(1) Traceback (most recent call last): - TypeError: onlyk() takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes exactly 0 positional arguments (1 given) >>> onlyk(1, 2) Traceback (most recent call last): - TypeError: onlyk() takes at most 0 positional arguments (2 given) + TypeError: onlyk() takes exactly 0 positional arguments (2 given) >>> onlyk(1, a=1, b=2) Traceback (most recent call last): - TypeError: onlyk() takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes exactly 0 positional arguments (1 given) >>> tk(a=1) (('a', 1),) -- 2.26.2