more accurate exception messages when passing a wrong argument count (adapted to...
authorStefan Behnel <scoder@users.berlios.de>
Fri, 22 Aug 2008 18:15:16 +0000 (20:15 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 22 Aug 2008 18:15:16 +0000 (20:15 +0200)
Cython/Compiler/Nodes.py
tests/run/classkwonlyargs.pyx
tests/run/extkwonlyargs.pyx
tests/run/extstarargs.pyx
tests/run/kwonlyargs.pyx
tests/run/kwonlyargscall.pyx
tests/run/simpcall.pyx
tests/run/starargs.pyx

index 729b4e6f4431d12fabd141bcd536f419568145d9..bf55c739f805e991647b5ec801d7e0bda47b5763 100644 (file)
@@ -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);
 }
 """]
 
index 5ab9a2dadce27c637d800edc7cd97e34e2b5108b..eac6c5c078980f2f2fc21e00677a828c4b1ff696 100644 (file)
@@ -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
index b819419c00999eca8b57633357954ad8daaebcf6..c3371e0314b1674b8b5c91307c5ea4ee8a8483e8 100644 (file)
@@ -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
index 0b785999dd5c76e1541dc1b9552728c889ec2e15..20708bd5c741f76afddffa930a855b69705e2991 100644 (file)
@@ -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),)
index b60ac4d5340596e933f0f9c5609e3132ed1f8532..e89caf170c336be9bcfec499dec50e8bf387ae9a 100644 (file)
@@ -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
index 296e512ed88a111bc957d821f0b21ce6b1430035..910a5c046b106d81796771fe3bc9dcfb1e6a7489 100644 (file)
@@ -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):
index e8f788ae5ab385cf38da80b0b411df211dd69b39..497df8b02f1959d32581d94018c2290539fb81a5 100644 (file)
@@ -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" '")
index d16a2d97045651fdf44265a8c088c4b0265cab00..b6910eea4ec158321265da519d691f33c347abd4 100644 (file)
@@ -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),)