]> granicus.if.org Git - python/commitdiff
allow unicode keyword arguments for the ** syntax #4978
authorBenjamin Peterson <benjamin@python.org>
Tue, 20 Jan 2009 14:21:16 +0000 (14:21 +0000)
committerBenjamin Peterson <benjamin@python.org>
Tue, 20 Jan 2009 14:21:16 +0000 (14:21 +0000)
Lib/test/test_extcall.py
Misc/NEWS
Python/ceval.c

index 8088fe2e8e4a25c6f1e844dd63bb012cd7976a24..f4ab204279a5855b473aab8091ca4f29a5ba1a91 100644 (file)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """Doctest for method/function calls.
 
 We're going the use these types for extra testing
@@ -252,11 +253,30 @@ TypeError if te dictionary is not empty
 
 """
 
+import unittest
 from test import test_support
 
+
+class UnicodeKeywordArgsTest(unittest.TestCase):
+
+    def test_unicode_keywords(self):
+        def f(a):
+            return a
+        self.assertEqual(f(**{u'a': 4}), 4)
+        self.assertRaises(TypeError, f, **{u'stören': 4})
+        self.assertRaises(TypeError, f, **{u'someLongString':2})
+        try:
+            f(a=4, **{u'a': 4})
+        except TypeError:
+            pass
+        else:
+            self.fail("duplicate arguments didn't raise")
+
+
 def test_main():
     from test import test_extcall # self import
     test_support.run_doctest(test_extcall, True)
+    test_support.run_unittest(UnicodeKeywordArgsTest)
 
 if __name__ == '__main__':
     test_main()
index c7be9e05ed84cf20ab30cc8204b653febb9e9962..f8bfa005e96616415b2aa66cd66cc71d073f040c 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,8 @@ What's New in Python 2.7 alpha 1
 Core and Builtins
 -----------------
 
+- Issue #4978: Passing keyword arguments as unicode strings is now allowed.
+
 - os.ftruncate raises OSErrors instead of IOErrors for consistency with other os
   functions.
 
index 88483a5f36b72126e902b23fc386a2ce21c43fd6..92a7653e05b70a599b303725fdf34a8871028a68 100644 (file)
@@ -127,6 +127,7 @@ static void reset_exc_info(PyThreadState *);
 static void format_exc_check_arg(PyObject *, char *, PyObject *);
 static PyObject * string_concatenate(PyObject *, PyObject *,
                                    PyFrameObject *, unsigned char *);
+static PyObject * kwd_as_string(PyObject *);
 
 #define NAME_ERROR_MSG \
        "name '%.200s' is not defined"
@@ -2932,7 +2933,8 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
                        PyObject *keyword = kws[2*i];
                        PyObject *value = kws[2*i + 1];
                        int j;
-                       if (keyword == NULL || !PyString_Check(keyword)) {
+                       if (keyword == NULL || !(PyString_Check(keyword) ||
+                                                PyUnicode_Check(keyword))) {
                                PyErr_Format(PyExc_TypeError,
                                    "%.200s() keywords must be strings",
                                    PyString_AsString(co->co_name));
@@ -2961,11 +2963,15 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
                                goto fail;
                        if (j >= co->co_argcount) {
                                if (kwdict == NULL) {
-                                       PyErr_Format(PyExc_TypeError,
-                                           "%.200s() got an unexpected "
-                                           "keyword argument '%.400s'",
-                                           PyString_AsString(co->co_name),
-                                           PyString_AsString(keyword));
+                                       PyObject *kwd_str = kwd_as_string(keyword);
+                                       if (kwd_str) {
+                                               PyErr_Format(PyExc_TypeError,
+                                                            "%.200s() got an unexpected "
+                                                            "keyword argument '%.400s'",
+                                                            PyString_AsString(co->co_name),
+                                                            PyString_AsString(kwd_str));
+                                               Py_DECREF(kwd_str);
+                                       }
                                        goto fail;
                                }
                                PyDict_SetItem(kwdict, keyword, value);
@@ -2973,12 +2979,16 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
                        }
 kw_found:
                        if (GETLOCAL(j) != NULL) {
-                               PyErr_Format(PyExc_TypeError,
-                                               "%.200s() got multiple "
-                                               "values for keyword "
-                                               "argument '%.400s'",
-                                               PyString_AsString(co->co_name),
-                                               PyString_AsString(keyword));
+                               PyObject *kwd_str = kwd_as_string(keyword);
+                               if (kwd_str) {
+                                       PyErr_Format(PyExc_TypeError,
+                                                    "%.200s() got multiple "
+                                                    "values for keyword "
+                                                    "argument '%.400s'",
+                                                    PyString_AsString(co->co_name),
+                                                    PyString_AsString(kwd_str));
+                                       Py_DECREF(kwd_str);
+                               }
                                goto fail;
                        }
                        Py_INCREF(value);
@@ -3105,6 +3115,17 @@ fail: /* Jump here from prelude on failure */
 }
 
 
+static PyObject *
+kwd_as_string(PyObject *kwd) {
+       if (PyString_Check(kwd)) {
+               Py_INCREF(kwd);
+               return kwd;
+       }
+       else
+               return _PyUnicode_AsDefaultEncodedString(kwd, "replace");
+}
+
+
 /* Implementation notes for set_exc_info() and reset_exc_info():
 
 - Below, 'exc_ZZZ' stands for 'exc_type', 'exc_value' and