fixed handling of keyword-only arguments
authorStefan Behnel <scoder@users.berlios.de>
Fri, 22 Aug 2008 10:28:15 +0000 (12:28 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 22 Aug 2008 10:28:15 +0000 (12:28 +0200)
changed more exception messages to what Py3 raises
some cleanup

Cython/Compiler/Nodes.py
tests/run/callargs.pyx
tests/run/classkwonlyargs.pyx
tests/run/extkwonlyargs.pyx
tests/run/kwonlyargs.pyx
tests/run/kwonlyargscall.pyx

index 77fb89cdf7bdd683113c01c06ea74f0921cd38ef..2a7b274360af43ebd887a9f3c2968798c3ce5b7f 100644 (file)
@@ -1562,7 +1562,7 @@ class DefNode(FuncDefNode):
                 if arg.is_generic:
                     code.put(
                         '"%s",' % 
-                            arg.name)
+                            arg.name.utf8encode())
                     if arg.kw_only and not arg.default:
                         has_reqd_kwds = 1
                         flag = "1"
@@ -1593,20 +1593,19 @@ class DefNode(FuncDefNode):
         has_kwonly_args = self.num_kwonly_args > 0
         has_star_or_kw_args = self.star_arg is not None \
             or self.starstar_arg is not None or has_kwonly_args
-            
+
         if not self.signature_has_generic_args():
             if has_star_or_kw_args:
                 error(self.pos, "This method cannot have * or keyword arguments")
             self.generate_argument_conversion_code(code)
-            
+
         elif not self.signature_has_nongeneric_args():
             # func(*args) or func(**kw) or func(*args, **kw)
             self.generate_stararg_copy_code(code)
-            
+
         else:
-            arg_addrs = []
-            arg_formats = []
             positional_args = []
+            kw_only_args = []
             default_seen = 0
             for arg in self.args:
                 arg_entry = arg.entry
@@ -1616,37 +1615,30 @@ class DefNode(FuncDefNode):
                             "%s = %s;" % (
                                 arg_entry.cname,
                                 arg.default_result_code))
-                        if not default_seen:
-                            arg_formats.append("|")
                         default_seen = 1
-                        if not arg.is_self_arg and not arg.kw_only:
-                            positional_args.append(arg)
+                        if not arg.is_self_arg:
+                            if arg.kw_only:
+                                kw_only_args.append(arg)
+                            else:
+                                positional_args.append(arg)
                     elif arg.kw_only:
-                        if not default_seen:
-                            arg_formats.append("|")
+                        kw_only_args.append(arg)
                         default_seen = 1
                     elif default_seen:
                         error(arg.pos, "Non-default argument following default argument")
                     elif not arg.is_self_arg:
                         positional_args.append(arg)
                     if arg.needs_conversion:
-                        arg_addrs.append("&" + arg.hdr_cname)
                         format = arg.hdr_type.parsetuple_format
                     else:
-                        arg_addrs.append("&" + arg_entry.cname)
                         format = arg_entry.type.parsetuple_format
-                    if format:
-                        arg_formats.append(format)
-                    else:
-                        error(arg.pos, 
-                            "Cannot convert Python object argument to type '%s' (when parsing input arguments)" 
-                                % arg.type)
-
-            if has_star_or_kw_args:
-                self.generate_stararg_getting_code(code)
+                    if not format:
+                        error(arg.pos,
+                              "Cannot convert Python object argument to type '%s' (when parsing input arguments)"
+                              % arg.type)
 
             self.generate_tuple_and_keyword_parsing_code(
-                positional_args, arg_formats, arg_addrs, code)
+                positional_args, kw_only_args, code)
 
         code.error_label = old_error_label
         if code.label_used(our_error_label):
@@ -1664,52 +1656,6 @@ class DefNode(FuncDefNode):
             code.putln("return %s;" % self.error_value())
             code.put_label(end_label)
 
-    def _generate_argument_tuple_parsing_code(self, positional_args,
-                                             arg_formats, arg_addrs, code):
-        # Unpack inplace if it's simple
-        if not self.num_required_kw_args:
-            min_positional_args = self.num_required_args - self.num_required_kw_args
-            max_positional_args = len(positional_args)
-            if len(self.args) > 0 and self.args[0].is_self_arg:
-                min_positional_args -= 1
-            if max_positional_args == min_positional_args:
-                count_cond = "likely(PyTuple_GET_SIZE(%s) == %s)" % (
-                    Naming.args_cname, max_positional_args)
-            else:
-                count_cond = "likely(%s <= PyTuple_GET_SIZE(%s)) && likely(PyTuple_GET_SIZE(%s) <= %s)" % (
-                               min_positional_args,
-                               Naming.args_cname,
-                               Naming.args_cname,
-                               max_positional_args)
-            code.putln(
-                'if (likely(!%s) && %s) {' % (Naming.kwds_cname, count_cond))
-            i = 0
-            closing = 0
-            for arg in positional_args:
-                if arg.default:
-                    code.putln('if (PyTuple_GET_SIZE(%s) > %s) {' % (Naming.args_cname, i))
-                    closing += 1
-                item = "PyTuple_GET_ITEM(%s, %s)" % (Naming.args_cname, i)
-                self.generate_arg_assignment(arg, item, code)
-                i += 1
-            for _ in range(closing):
-                code.putln('}')
-            code.putln(
-                '}')
-            code.putln('else {')
-
-        argformat = '"%s"' % string.join(arg_formats, "")
-        pt_arglist = [Naming.args_cname, Naming.kwds_cname, argformat, Naming.kwdlist_cname] + arg_addrs
-        pt_argstring = string.join(pt_arglist, ", ")
-        code.putln(
-            'if (unlikely(!PyArg_ParseTupleAndKeywords(%s))) %s' % (
-                pt_argstring,
-                code.error_goto(self.pos)))
-        self.generate_argument_conversion_code(code)
-
-        if not self.num_required_kw_args:
-            code.putln('}')
-
     def generate_arg_assignment(self, arg, item, code):
         if arg.type.is_pyobject:
             if arg.is_generic:
@@ -1748,8 +1694,12 @@ class DefNode(FuncDefNode):
 
     def generate_stararg_copy_code(self, code):
         if not self.star_arg:
-            self.generate_positional_args_check(code, 0)
-        self.generate_keyword_args_check(code)
+            self.generate_positional_args_check(0, code)
+
+        code.putln(
+            "if (unlikely(%s) && unlikely(!__Pyx_CheckKeywordStrings(%s, \"%s\", %d))) return %s;" % (
+                Naming.kwds_cname, Naming.kwds_cname, self.name,
+                bool(self.starstar_arg), self.error_value()))
 
         if self.starstar_arg:
             code.putln("%s = (%s) ? PyDict_Copy(%s) : PyDict_New();" % (
@@ -1769,16 +1719,11 @@ class DefNode(FuncDefNode):
             self.star_arg.entry.xdecref_cleanup = 0
             self.star_arg = None
 
-    def generate_stararg_getting_code(self, code):
-        num_kwonly = self.num_kwonly_args
-        fixed_args = self.entry.signature.num_fixed_args()
-        nargs = len(self.args) - num_kwonly - fixed_args
-        error_return = "return %s;" % self.error_value()
-
+    def generate_stararg_getting_code(self, max_positional_args, code):
         if self.star_arg:
             star_arg_cname = self.star_arg.entry.cname
             code.putln("if (likely(PyTuple_GET_SIZE(%s) <= %d)) {" % (
-                    Naming.args_cname, nargs))
+                    Naming.args_cname, max_positional_args))
             code.put_incref(Naming.args_cname, py_object_type)
             code.put("%s = %s; " % (star_arg_cname, Naming.empty_tuple))
             code.put_incref(Naming.empty_tuple, py_object_type)
@@ -1786,18 +1731,12 @@ class DefNode(FuncDefNode):
             code.putln(
                 "if (unlikely(__Pyx_SplitStarArg(&%s, %d, &%s) < 0)) return %s;" % (
                     Naming.args_cname,
-                    nargs,
+                    max_positional_args,
                     star_arg_cname,
                     self.error_value()))
             code.putln("}")
             self.star_arg.entry.xdecref_cleanup = 0
-        else:
-            # make sure supernumerous positional arguments do not run
-            # into keyword-only arguments and provide a more helpful
-            # message than PyArg_ParseTupelAndKeywords()
-            self.generate_positional_args_check(code, nargs)
 
-        handle_error = 0
         if self.starstar_arg:
             handle_error = 1
             code.put(
@@ -1809,25 +1748,7 @@ class DefNode(FuncDefNode):
                     Naming.args_cname,
                     self.name.utf8encode()))
             self.starstar_arg.entry.xdecref_cleanup = 0
-        elif self.num_required_kw_args:
-            handle_error = 1
-            code.put('if (unlikely(__Pyx_CheckRequiredKeywords(%s, %s, %s, PyTuple_GET_SIZE(%s), "%s") < 0)) ' % (
-                    Naming.kwds_cname,
-                    Naming.kwdlist_cname,
-                    self.reqd_kw_flags_cname,
-                    Naming.args_cname,
-                    self.name.utf8encode()))
-        else:
-            # check that positional arguments are not passed as keywords also
-            handle_error = 1
-            code.put('if (unlikely(%s) && unlikely(__Pyx_CheckDoubleKeywords(%s, %s, PyTuple_GET_SIZE(%s), "%s") < 0)) ' % (
-                    Naming.kwds_cname,
-                    Naming.kwds_cname,
-                    Naming.kwdlist_cname,
-                    Naming.args_cname,
-                    self.name.utf8encode()))
-
-        if handle_error:
+            error_return = "return %s;" % self.error_value()
             if self.star_arg:
                 code.putln("{")
                 code.put_decref(Naming.args_cname, py_object_type)
@@ -1838,29 +1759,28 @@ class DefNode(FuncDefNode):
                 code.putln(error_return)
 
     def generate_tuple_and_keyword_parsing_code(self, positional_args,
-                                                arg_formats, arg_addrs, code):
+                                                kw_only_args, code):
         min_positional_args = self.num_required_args - self.num_required_kw_args
         if len(self.args) > 0 and self.args[0].is_self_arg:
             min_positional_args -= 1
         max_positional_args = len(positional_args)
+        max_args = max_positional_args + len(kw_only_args)
 
-        # just a quick check to start with
         if not self.star_arg:
-            code.putln("if (unlikely(PyTuple_GET_SIZE(%s) > %d)) {" % (
-                    Naming.args_cname, max_positional_args))
-            code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, PyTuple_GET_SIZE(%s)); ' % (
-                    self.name.utf8encode(), min_positional_args,
-                    max_positional_args, Naming.args_cname))
-            code.putln(code.error_goto(self.pos))
-            code.putln("}")
+            self.generate_positional_args_check(max_positional_args, code)
+
+        if self.star_arg or self.starstar_arg:
+            self.generate_stararg_getting_code(max_positional_args, code)
 
         # --- optimised code when we receive keyword arguments
         code.putln("if (unlikely(%s) && (PyDict_Size(%s) > 0)) {" % (
                 Naming.kwds_cname, Naming.kwds_cname))
-        code.putln("PyObject* values[%d];" % max_positional_args)
-        code.putln("Py_ssize_t kw_args = PyDict_Size(%s), arg;" %
+        code.putln("PyObject* values[%d];" % max_args)
+        code.putln("Py_ssize_t arg, kw_args = PyDict_Size(%s);" %
                    Naming.kwds_cname)
 
+        # parse arg tuple and check that positional args are not also
+        # passed as kw args
         code.putln("for (arg=0; arg < PyTuple_GET_SIZE(%s); arg++) {" %
                    Naming.args_cname)
         code.putln("values[arg] = PyTuple_GET_ITEM(%s, arg);" %
@@ -1873,21 +1793,27 @@ class DefNode(FuncDefNode):
         code.putln('}')
         code.putln('}')
 
+        # parse remaining positional args from the keyword dictionary
         code.putln("for (arg=PyTuple_GET_SIZE(%s); arg < %d; arg++) {" % (
-                Naming.args_cname, max_positional_args))
+                Naming.args_cname, max_args))
         code.putln('values[arg] = PyDict_GetItemString(%s, %s[arg]);' % (
                 Naming.kwds_cname, Naming.kwdlist_cname))
         code.putln('if (values[arg]) kw_args--;');
         code.putln('}')
 
+        # raise an error if not all keywords were read
         code.putln('if (unlikely(kw_args > 0)) {')
         code.put('if (!__Pyx_CheckKeywords(%s, "%s", %s)) ' % (
                 Naming.kwds_cname, self.name.utf8encode(), Naming.kwdlist_cname))
         code.putln(code.error_goto(self.pos))
         code.putln('}')
 
+        # convert arg values to their final type and assign them
         default_seen = False
-        for i, arg in enumerate(positional_args):
+        i = 0
+        for arg in self.args:
+            if arg.is_self_arg:
+                continue
             if arg.default:
                 default_seen = True
             if default_seen:
@@ -1896,60 +1822,62 @@ class DefNode(FuncDefNode):
             if default_seen:
                 if not arg.default:
                     code.putln('} else {')
-                    code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d); ' % (
-                            self.name.utf8encode(), min_positional_args,
-                            max_positional_args, i))
+                    if arg.kw_only:
+                        code.put('PyErr_Format(PyExc_TypeError, "%s() needs keyword-only argument %s"); ' % (
+                                 self.name.utf8encode(), arg.name.utf8encode()))
+                    else:
+                        code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d); ' % (
+                                self.name.utf8encode(), min_positional_args,
+                                max_positional_args, i))
                     code.putln(code.error_goto(self.pos))
                 code.putln('}')
+            i += 1
 
-        # --- optimised code when we did not receive any keyword arguments
-        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,
-                max_positional_args, Naming.args_cname))
-        code.putln(code.error_goto(self.pos))
-
-        code.putln('} else {')
-        closing = 0
-        for i, arg in enumerate(positional_args):
-            if arg.default:
-                code.putln('if (PyTuple_GET_SIZE(%s) > %s) {' % (Naming.args_cname, i))
-                closing += 1
-            item = "PyTuple_GET_ITEM(%s, %s)" % (Naming.args_cname, i)
-            self.generate_arg_assignment(arg, item, code)
-        for _ in range(closing):
+        # --- optimised code when we do not receive any keyword arguments
+        if self.num_required_kw_args:
+            # simple case: keywords required but none passed
+            for arg in self.args:
+                if arg.kw_only and not arg.default:
+                    required_arg = arg
+            code.putln('} else {')
+            code.put('PyErr_Format(PyExc_TypeError, "%s() needs keyword-only argument %s");' % (
+                     self.name.utf8encode(), required_arg.name.utf8encode()))
+            code.putln(code.error_goto(self.pos))
             code.putln('}')
+        else:
+            # check if we have all required positional arguments
+            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,
+                    max_positional_args, Naming.args_cname))
+            code.putln(code.error_goto(self.pos))
 
-        code.putln('}')
-        return
-
-        argformat = '"%s"' % string.join(arg_formats, "")
-        pt_arglist = [Naming.args_cname, Naming.kwds_cname, argformat, Naming.kwdlist_cname] + arg_addrs
-        pt_argstring = string.join(pt_arglist, ", ")
-        code.putln(
-            'if (unlikely(!PyArg_ParseTupleAndKeywords(%s))) %s' % (
-                pt_argstring,
-                code.error_goto(self.pos)))
-        self.generate_argument_conversion_code(code)
+            # parse all positional arguments from the args tuple
+            code.putln('} else {')
+            closing = 0
+            for i, arg in enumerate(positional_args):
+                if arg.default:
+                    code.putln('if (PyTuple_GET_SIZE(%s) > %s) {' % (Naming.args_cname, i))
+                    closing += 1
+                item = "PyTuple_GET_ITEM(%s, %s)" % (Naming.args_cname, i)
+                self.generate_arg_assignment(arg, item, code)
+            for _ in range(closing):
+                code.putln('}')
 
-        code.putln('}')
+            code.putln('}')
+        return
 
-    def generate_positional_args_check(self, code, nargs):
+    def generate_positional_args_check(self, max_positional_args, 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, nargs))
+                Naming.args_cname, max_positional_args))
         code.putln('__Pyx_RaiseArgtupleInvalid("%s", 0, %d, PyTuple_GET_SIZE(%s));' % (
-                self.name.utf8encode(), nargs, Naming.args_cname))
+                self.name.utf8encode(), max_positional_args, Naming.args_cname))
         code.putln("return %s;" % self.error_value())
         code.putln("}")
 
-    def generate_keyword_args_check(self, code):
-        code.putln("if (unlikely(%s)) {" % Naming.kwds_cname)
-        code.putln("if (unlikely(!__Pyx_CheckKeywordStrings(%s, \"%s\", %d))) return %s;" % (
-                Naming.kwds_cname, self.name,
-                bool(self.starstar_arg), self.error_value()))
-        code.putln("}")
-
     def generate_argument_conversion_code(self, code):
         # Generate code to convert arguments from
         # signature type to declared type, if needed.
@@ -4612,7 +4540,7 @@ arg_passed_twice:
     goto bad;
 missing_kwarg:
     PyErr_Format(PyExc_TypeError,
-        "required keyword argument '%s' is missing", *p);
+        "%s() needs keyword-only argument %s", func_name, *p);
 bad:
     Py_XDECREF(s);
     Py_XDECREF(kwds1);
index 27d46cabbe623e52b2b5a10a2eb7b0b22ccd0f4e..6976a993840fec0e42512fbc9e25a903b40e2871 100644 (file)
@@ -1,15 +1,67 @@
 __doc__ = u"""
-  >>> test()
+  >>> test_pos_args(h)
   1 2 3 * 0 0
+  1 2 9 * 2 0
+  1 2 7 * 2 0
+  9 8 7 * 0 0
+  7 8 9 * 0 0
+
+  >>> test_kw_args(h)
   1 2 3 * 0 0
   1 2 9 * 2 1
   1 2 7 * 2 1
   1 2 9 * 2 2
   1 2 9 * 2 2
   1 2 9 * 2 3
+
+  >>> test_kw_args(e)
+  2 1
+  5 1
+  5 1
+  5 2
+  5 2
+  5 3
+
+  >>> test_kw(e)
+  0 1
+  0 2
+  0 2
+  0 1
+
+  >>> test_kw(g)
+  1
+  2
+  2
+  1
+
+  >>> test_pos_args(f)
+  3
+  5
+  5
+  3
+  3
+
+  >>> test_noargs(e)
+  0 0
+  >>> test_noargs(f)
+  0
+  >>> test_noargs(g)
+  0
+  >>> test_noargs(h)
+  Traceback (most recent call last):
+  TypeError: h() takes at least 3 positional arguments (0 given)
 """
 
-def f(a, b, c, *args, **kwargs):
+def e(*args, **kwargs):
+    print len(args), len(kwargs)
+
+def f(*args):
+    print len(args)
+
+def g(**kwargs):
+    print len(kwargs)
+
+def h(a, b, c, *args, **kwargs):
     print a, b, c, u'*', len(args), len(kwargs)
 
 args = (9,8,7)
@@ -21,11 +73,26 @@ else:
     kwargs = {"test" : u"toast"}
 
 
-def test():
-    f(1,2,3)
+def test_kw_args(f):
     f(1,2, c=3)
     f(1,2, d=3, *args)
     f(1,2, d=3, *(7,8,9))
     f(1,2, d=3, *args, **kwargs)
     f(1,2, d=3, *args, e=5)
     f(1,2, d=3, *args, e=5, **kwargs)
+
+def test_pos_args(f):
+    f(1,2,3)
+    f(1,2, *args)
+    f(1,2, *(7,8,9))
+    f(*args)
+    f(*(7,8,9))
+
+def test_kw(f):
+    f(c=3)
+    f(d=3, e=5)
+    f(d=3, **kwargs)
+    f(**kwargs)
+
+def test_noargs(f):
+    f()
index 2c2b1e2b020c0880b124aeae5a2d5ca7b0755240..5ab9a2dadce27c637d800edc7cd97e34e2b5108b 100644 (file)
@@ -40,7 +40,7 @@ __doc__ = u"""
     TypeError: f() takes at most 3 positional arguments (4 given)
     >>> f(1,2)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: f() needs keyword-only argument c
     >>> f(1,2, c=1, e=2)
     Traceback (most recent call last):
     TypeError: 'e' is an invalid keyword argument for this function
@@ -54,10 +54,10 @@ __doc__ = u"""
     TypeError: g() takes at most 3 positional arguments (4 given)
     >>> g(1,2)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: g() needs keyword-only argument c
     >>> g(1,2, c=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: g() needs keyword-only argument f
 
     >>> h(1,2, c=1, f=2)
     >>> h(1,2, c=1, f=2, e=3)
@@ -66,10 +66,10 @@ __doc__ = u"""
 
     >>> h(1,2,3)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
     >>> h(1,2, d=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
 
     >>> k(1,2, c=1, f=2)
     >>> k(1,2, c=1, f=2, e=3)
@@ -78,10 +78,10 @@ __doc__ = u"""
 
     >>> k(1,2,3)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
     >>> k(1,2, d=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
 """
 
 import sys, re
index b2d343ee738807f3806b05ad557847e03060bce2..b819419c00999eca8b57633357954ad8daaebcf6 100644 (file)
@@ -40,7 +40,7 @@ __doc__ = u"""
     TypeError: f() takes at most 2 positional arguments (3 given)
     >>> f(1,2)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: f() needs keyword-only argument c
     >>> f(1,2, c=1, e=2)
     Traceback (most recent call last):
     TypeError: 'e' is an invalid keyword argument for this function
@@ -54,10 +54,10 @@ __doc__ = u"""
     TypeError: g() takes at most 2 positional arguments (3 given)
     >>> g(1,2)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: g() needs keyword-only argument c
     >>> g(1,2, c=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: g() needs keyword-only argument f
 
     >>> h(1,2, c=1, f=2)
     >>> h(1,2, c=1, f=2, e=3)
@@ -66,10 +66,10 @@ __doc__ = u"""
 
     >>> h(1,2,3)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
     >>> h(1,2, d=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
 
     >>> k(1,2, c=1, f=2)
     >>> k(1,2, c=1, f=2, e=3)
@@ -78,10 +78,10 @@ __doc__ = u"""
 
     >>> k(1,2,3)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
     >>> k(1,2, d=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
 """
 
 cdef class Ext:
index 710dc476653408691fa1d6cac2570a6d83a675c4..b60ac4d5340596e933f0f9c5609e3132ed1f8532 100644 (file)
@@ -37,7 +37,7 @@ __doc__ = u"""
     TypeError: f() takes at most 2 positional arguments (3 given)
     >>> f(1,2)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: f() needs keyword-only argument c
     >>> f(1,2, c=1, e=2)
     Traceback (most recent call last):
     TypeError: 'e' is an invalid keyword argument for this function
@@ -51,10 +51,10 @@ __doc__ = u"""
     TypeError: g() takes at most 2 positional arguments (3 given)
     >>> g(1,2)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: g() needs keyword-only argument c
     >>> g(1,2, c=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: g() needs keyword-only argument f
 
     >>> h(1,2, c=1, f=2)
     >>> h(1,2, c=1, f=2, e=3)
@@ -63,10 +63,10 @@ __doc__ = u"""
 
     >>> h(1,2,3)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
     >>> h(1,2, d=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
 
     >>> k(1,2, c=1, f=2)
     >>> k(1,2, c=1, f=2, e=3)
@@ -75,10 +75,10 @@ __doc__ = u"""
 
     >>> k(1,2,3)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
     >>> k(1,2, d=1)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
 """
 
 def b(a, b, c):
index 415ee88a71955b8e4235d72985b24f8e89c145b1..296e512ed88a111bc957d821f0b21ce6b1430035 100644 (file)
@@ -1,17 +1,22 @@
 __doc__ = u"""
     >>> call3(b)
+    1 2 3
     >>> call4(b)
     Traceback (most recent call last):
     TypeError: b() takes at most 3 positional arguments (4 given)
 
     >>> call2(c)
+    1 2 1
     >>> call3(c)
+    1 2 3
     >>> call4(c)
     Traceback (most recent call last):
     TypeError: c() takes at most 3 positional arguments (4 given)
 
     >>> call2(d)
+    1 2 88
     >>> call2c(d)
+    1 2 1
 
     >>> call3(d)
     Traceback (most recent call last):
@@ -21,64 +26,82 @@ __doc__ = u"""
     TypeError: 'd' is an invalid keyword argument for this function
 
     >>> call2(e)
+    1 2 88 []
     >>> call2c(e)
+    1 2 1 []
     >>> call2d(e)
+    1 2 88 [('d', 1)]
     >>> call2cde(e)
+    1 2 1 [('d', 2), ('e', 3)]
     >>> call3(e)
+    1 2 3 []
     >>> call4(e)
     Traceback (most recent call last):
     TypeError: e() takes at most 3 positional arguments (4 given)
 
     >>> call2c(f)
+    1 2 1 42
     >>> call2cd(f)
+    1 2 1 2
 
     >>> call3(f)
     Traceback (most recent call last):
     TypeError: f() takes at most 2 positional arguments (3 given)
     >>> call2(f)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: f() needs keyword-only argument c
     >>> call2ce(f)
     Traceback (most recent call last):
     TypeError: 'e' is an invalid keyword argument for this function
 
     >>> call2cf(g)
+    1 2 1 42 17 2 []
     >>> call2cefd(g)
+    1 2 1 11 0 2 []
     >>> call2cfex(g)
+    1 2 1 42 0 2 [('x', 25)]
 
     >>> call3(g)
     Traceback (most recent call last):
     TypeError: g() takes at most 2 positional arguments (3 given)
     >>> call2(g)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: g() needs keyword-only argument c
     >>> call2c(g)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: g() needs keyword-only argument f
 
     >>> call2cf(h)
+    1 2 1 42 17 2 () []
     >>> call2cfe(h)
+    1 2 1 42 3 2 () []
     >>> call6cf(h)
+    1 2 1 42 17 2 (3, 4, 5, 6) []
     >>> call6cfexy(h)
+    1 2 1 42 3 2 (3, 4, 5, 6) [('x', 25), ('y', 11)]
 
     >>> call3(h)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
     >>> call3d(h)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'c' is missing
+    TypeError: h() needs keyword-only argument c
 
     >>> call2cf(k)
+    1 2 1 42 17 2 () []
     >>> call2cfe(k)
+    1 2 1 42 3 2 () []
     >>> call6df(k)
+    1 2 3 1 17 2 (4, 5, 6) []
     >>> call6dfexy(k)
+    1 2 3 1 3 2 (4, 5, 6) [('x', 25), ('y', 11)]
 
     >>> call3(k)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
     >>> call2d(k)
     Traceback (most recent call last):
-    TypeError: required keyword argument 'f' is missing
+    TypeError: k() needs keyword-only argument f
 """
 
 import sys, re
@@ -145,25 +168,33 @@ def call6dfexy(f):
 # the called functions:
 
 def b(a, b, c):
-    pass
+    print a,b,c
 
 def c(a, b, c=1):
-    pass
+    print a,b,c
 
 def d(a, b, *, c = 88):
-    pass
+    print a,b,c
 
 def e(a, b, c = 88, **kwds):
-    pass
+    kwlist = list(kwds.items())
+    kwlist.sort()
+    print a,b,c, kwlist
 
 def f(a, b, *, c, d = 42):
-    pass
+    print a,b,c,d
 
 def g(a, b, *, c, d = 42, e = 17, f, **kwds):
-    pass
+    kwlist = list(kwds.items())
+    kwlist.sort()
+    print a,b,c,d,e,f, kwlist
 
 def h(a, b, *args, c, d = 42, e = 17, f, **kwds):
-    pass
+    kwlist = list(kwds.items())
+    kwlist.sort()
+    print a,b,c,d,e,f, args, kwlist
 
 def k(a, b, c=1, *args, d = 42, e = 17, f, **kwds):
-    pass
+    kwlist = list(kwds.items())
+    kwlist.sort()
+    print a,b,c,d,e,f, args, kwlist