cleanup of arg parsing code, faster string comparison in __Pyx_SplitKeywords(), some...
authorStefan Behnel <scoder@users.berlios.de>
Tue, 26 Aug 2008 11:54:57 +0000 (13:54 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Tue, 26 Aug 2008 11:54:57 +0000 (13:54 +0200)
Cython/Compiler/Nodes.py
tests/run/callargs.pyx
tests/run/kwonlyargscall.pyx

index 61bd9760359d9f4a6661b0accb5a4b01ac1a4fa1..47da9188ee48b1c49ed37f3702e669c0e0e67ed3 100644 (file)
@@ -1732,6 +1732,8 @@ class DefNode(FuncDefNode):
         if len(self.args) > 0 and self.args[0].is_self_arg:
             min_positional_args -= 1
         max_positional_args = len(positional_args)
+        has_fixed_positional_count = not self.star_arg and \
+            min_positional_args == max_positional_args
 
         code.globalstate.use_utility_code(raise_double_keywords_utility_code)
         code.globalstate.use_utility_code(raise_argtuple_invalid_utility_code)
@@ -1749,6 +1751,7 @@ class DefNode(FuncDefNode):
                     Naming.kwds_cname, Naming.kwds_cname))
         self.generate_keyword_unpacking_code(
             min_positional_args, max_positional_args,
+            has_fixed_positional_count,
             positional_args, kw_only_args, argtuple_error_label, code)
 
         # --- optimised code when we do not receive any keyword arguments
@@ -1812,8 +1815,6 @@ class DefNode(FuncDefNode):
         if code.label_used(argtuple_error_label):
             code.put_goto(success_label)
             code.put_label(argtuple_error_label)
-            has_fixed_positional_count = not self.star_arg and \
-                min_positional_args == max_positional_args
             code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, PyTuple_GET_SIZE(%s)); ' % (
                     self.name.utf8encode(), has_fixed_positional_count,
                     min_positional_args, max_positional_args,
@@ -1850,8 +1851,8 @@ class DefNode(FuncDefNode):
             code.putln('}')
 
     def generate_keyword_unpacking_code(self, min_positional_args, max_positional_args,
-                                        positional_args, kw_only_args,
-                                        argtuple_error_label, code):
+                                        has_fixed_positional_count, positional_args,
+                                        kw_only_args, argtuple_error_label, code):
         all_args = tuple(positional_args) + tuple(kw_only_args)
         max_args = len(all_args)
 
@@ -1886,8 +1887,20 @@ class DefNode(FuncDefNode):
                     i, Naming.kwds_cname, Naming.pykwdlist_cname, i))
             if i < min_positional_args:
                 code.putln('if (likely(values[%d])) kw_args--;' % i);
-                code.put('else ')
-                code.put_goto(argtuple_error_label)
+                if i == 0:
+                    # special case: we know arg 0 is missing
+                    code.put('else ')
+                    code.put_goto(argtuple_error_label)
+                else:
+                    # provide the correct number of values (args or
+                    # kwargs) that were passed into positional
+                    # arguments up to this point
+                    code.putln('else {')
+                    code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, %d); ' % (
+                            self.name.utf8encode(), has_fixed_positional_count,
+                            min_positional_args, max_positional_args, i))
+                    code.putln(code.error_goto(self.pos))
+                    code.putln('}')
             else:
                 code.putln('if (values[%d]) kw_args--;' % i);
                 if arg.kw_only and not arg.default:
@@ -4323,40 +4336,6 @@ static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed
 }
 """]
 
-#------------------------------------------------------------------------------------
-#
-#  __Pyx_SplitStarArg splits the args tuple into two parts, one part
-#  suitable for passing to PyArg_ParseTupleAndKeywords, and the other
-#  containing any extra arguments. On success, replaces the borrowed
-#  reference *args with references to a new tuple, and passes back a
-#  new reference in *args2.  Does not touch any of its arguments on
-#  failure.
-
-split_stararg_utility_code = [
-"""
-static INLINE int __Pyx_SplitStarArg(PyObject **args, Py_ssize_t nargs, PyObject **args2); /*proto*/
-""","""
-static INLINE int __Pyx_SplitStarArg(
-    PyObject **args, 
-    Py_ssize_t nargs,
-    PyObject **args2)
-{
-    PyObject *args1 = 0;
-    args1 = PyTuple_GetSlice(*args, 0, nargs);
-    if (!args1) {
-        *args2 = 0;
-        return -1;
-    }
-    *args2 = PyTuple_GetSlice(*args, nargs, PyTuple_GET_SIZE(*args));
-    if (!*args2) {
-        Py_DECREF(args1);
-        return -1;
-    }
-    *args = args1;
-    return 0;
-}
-"""]
-
 #------------------------------------------------------------------------------------
 #
 #  __Pyx_RaiseArgtupleInvalid raises the correct exception when too
@@ -4365,11 +4344,11 @@ static INLINE int __Pyx_SplitStarArg(
 
 raise_argtuple_invalid_utility_code = [
 """
-static void __Pyx_RaiseArgtupleInvalid(char* func_name, int exact,
+static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact,
     Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/
 ""","""
 static void __Pyx_RaiseArgtupleInvalid(
-    char* func_name,
+    const char* func_name,
     int exact,
     Py_ssize_t num_min,
     Py_ssize_t num_max,
@@ -4403,10 +4382,10 @@ static void __Pyx_RaiseArgtupleInvalid(
 
 raise_keyword_required_utility_code = [
 """
-static INLINE void __Pyx_RaiseKeywordRequired(char* func_name, PyObject* kw_name); /*proto*/
+static INLINE void __Pyx_RaiseKeywordRequired(const char* func_name, PyObject* kw_name); /*proto*/
 ""","""
 static INLINE void __Pyx_RaiseKeywordRequired(
-    char* func_name,
+    const char* func_name,
     PyObject* kw_name)
 {
     PyErr_Format(PyExc_TypeError,
@@ -4501,16 +4480,16 @@ static int __Pyx_CheckKeywordStrings(
 split_keywords_utility_code = [
 """
 static int __Pyx_SplitKeywords(PyObject *kwds, PyObject **argnames[], \
-    PyObject *kwds2, Py_ssize_t num_pos_args, char* function_name); /*proto*/
+    PyObject *kwds2, Py_ssize_t num_pos_args, const char* function_name); /*proto*/
 ""","""
 static int __Pyx_SplitKeywords(
     PyObject *kwds,
     PyObject **argnames[],
     PyObject *kwds2,
     Py_ssize_t num_pos_args,
-    char* function_name)
+    const char* function_name)
 {
-    PyObjectkey = 0, *value = 0;
+    PyObject *key = 0, *value = 0;
     Py_ssize_t pos = 0;
     PyObject*** name;
 
@@ -4520,18 +4499,18 @@ static int __Pyx_SplitKeywords(
         #else
         if (unlikely(!PyUnicode_CheckExact(key)) && unlikely(!PyUnicode_Check(key))) {
         #endif
-            PyErr_Format(PyExc_TypeError,
-                         "%s() keywords must be strings", function_name);
-            return 0;
+            goto invalid_keyword_type;
         } else {
             name = argnames;
             while (*name && (**name != key)) name++;
             if (!*name) {
                 for (name = argnames; *name; name++) {
                     #if PY_MAJOR_VERSION >= 3
-                    if (PyUnicode_Compare(**name, key) == 0) break;
+                    if (PyUnicode_GET_SIZE(**name) == PyUnicode_GET_SIZE(key)) &&
+                        PyUnicode_Compare(**name, key) == 0) break;
                     #else
-                    if (strcmp(PyString_AS_STRING(**name),
+                    if (PyString_GET_SIZE(**name) == PyString_GET_SIZE(key) &&
+                        strcmp(PyString_AS_STRING(**name),
                                PyString_AS_STRING(key)) == 0) break;
                     #endif
                 }
@@ -4539,19 +4518,23 @@ static int __Pyx_SplitKeywords(
                     if (kwds2) {
                         if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad;
                     } else {
-                        goto split_kw_invalid_keyword;
+                        goto invalid_keyword;
                     }
                 }
             }
             if (*name && ((name-argnames) < num_pos_args))
-                goto split_kw_arg_passed_twice;
+                goto arg_passed_twice;
             }
     }
     return 0;
-split_kw_arg_passed_twice:
+arg_passed_twice:
     __Pyx_RaiseDoubleKeywordsError(function_name, **name);
     goto bad;
-split_kw_invalid_keyword:
+invalid_keyword_type:
+    PyErr_Format(PyExc_TypeError,
+        "%s() keywords must be strings", function_name);
+    goto bad;
+invalid_keyword:
     PyErr_Format(PyExc_TypeError,
     #if PY_MAJOR_VERSION < 3
         "'%s' is an invalid keyword argument for this function",
index e2e6855e7c5ac4af99a58f4727c6e6eeea73edd5..9ec9f8af73faa6c74ccdb2f48c345370cb0deec9 100644 (file)
@@ -57,6 +57,19 @@ __doc__ = u"""
   >>> h(1,2, d=5)
   Traceback (most recent call last):
   TypeError: h() takes at least 3 positional arguments (2 given)
+
+  >>> test_int_kwargs(e)
+  Traceback (most recent call last):
+  TypeError: e() keywords must be strings
+  >>> test_int_kwargs(f)
+  Traceback (most recent call last):
+  TypeError: f() keywords must be strings
+  >>> test_int_kwargs(g)
+  Traceback (most recent call last):
+  TypeError: g() keywords must be strings
+  >>> test_int_kwargs(h)
+  Traceback (most recent call last):
+  TypeError: h() keywords must be strings
 """
 
 def e(*args, **kwargs):
@@ -103,3 +116,6 @@ def test_kw(f):
 
 def test_noargs(f):
     f()
+
+def test_int_kwargs(f):
+    f(a=1,b=2,c=3, **{10:20,30:40})
index f05473e83256fdd37ed5ffd756e2e432d98897a8..0c8592f9e965371a8e1f23154d27fd461b152224 100644 (file)
@@ -1,4 +1,7 @@
 __doc__ = u"""
+    >>> call0ab(b)
+    Traceback (most recent call last):
+    TypeError: b() takes exactly 3 positional arguments (2 given)
     >>> call0abc(b)
     1 2 3
     >>> call3(b)
@@ -21,6 +24,8 @@ __doc__ = u"""
 
     >>> call0abc(d)
     1 2 3
+    >>> call0ab(d)
+    1 2 88
     >>> call2(d)
     1 2 88
     >>> call2c(d)