[Python-checkins] r80450 - in python/branches/py3k: Doc/c-api/arg.rst Include/dictobject.h Include/modsupport.h Lib/test/test_dict.py Misc/NEWS Objects/dictobject.c Python/getargs.c

benjamin.peterson python-checkins at python.org
Sat Apr 24 20:21:18 CEST 2010


Author: benjamin.peterson
Date: Sat Apr 24 20:21:17 2010
New Revision: 80450

Log:
prevent the dict constructor from accepting non-string keyword args #8419

This adds PyArg_ValidateKeywordArguments, which checks that keyword arguments
are all strings, using an optimized method if possible.



Modified:
   python/branches/py3k/Doc/c-api/arg.rst
   python/branches/py3k/Include/dictobject.h
   python/branches/py3k/Include/modsupport.h
   python/branches/py3k/Lib/test/test_dict.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Objects/dictobject.c
   python/branches/py3k/Python/getargs.c

Modified: python/branches/py3k/Doc/c-api/arg.rst
==============================================================================
--- python/branches/py3k/Doc/c-api/arg.rst	(original)
+++ python/branches/py3k/Doc/c-api/arg.rst	Sat Apr 24 20:21:17 2010
@@ -366,6 +366,13 @@
    va_list rather than a variable number of arguments.
 
 
+.. cfunction:: int PyArg_ValidateKeywordArguments(PyObject *)
+
+   Ensure that the keys in the keywords argument dictionary are strings.  This
+   is only needed if :cfunc:`PyArg_ParseTupleAndKeywords` is not used, since the
+   latter already does this check.
+
+
 .. XXX deprecated, will be removed
 .. cfunction:: int PyArg_Parse(PyObject *args, const char *format, ...)
 

Modified: python/branches/py3k/Include/dictobject.h
==============================================================================
--- python/branches/py3k/Include/dictobject.h	(original)
+++ python/branches/py3k/Include/dictobject.h	Sat Apr 24 20:21:17 2010
@@ -126,6 +126,7 @@
 PyAPI_FUNC(int) _PyDict_Contains(PyObject *mp, PyObject *key, long hash);
 PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
 PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp);
+PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp);
 
 /* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */
 PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other);

Modified: python/branches/py3k/Include/modsupport.h
==============================================================================
--- python/branches/py3k/Include/modsupport.h	(original)
+++ python/branches/py3k/Include/modsupport.h	Sat Apr 24 20:21:17 2010
@@ -27,6 +27,7 @@
 PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);
 PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
                                                   const char *, char **, ...);
+PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *);
 PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...);
 PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
 PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);

Modified: python/branches/py3k/Lib/test/test_dict.py
==============================================================================
--- python/branches/py3k/Lib/test/test_dict.py	(original)
+++ python/branches/py3k/Lib/test/test_dict.py	Sat Apr 24 20:21:17 2010
@@ -7,6 +7,12 @@
 
 class DictTest(unittest.TestCase):
 
+    def test_invalid_keyword_arguments(self):
+        with self.assertRaises(TypeError):
+            dict(**{1 : 2})
+        with self.assertRaises(TypeError):
+            {}.update(**{1 : 2})
+
     def test_constructor(self):
         # calling built-in types without argument must return empty
         self.assertEqual(dict(), {})

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Sat Apr 24 20:21:17 2010
@@ -12,6 +12,9 @@
 Core and Builtins
 -----------------
 
+- Issue #8419: Prevent the dict constructor from accepting non-string keyword
+  arguments.
+
 - Issue #8124: PySys_WriteStdout() and PySys_WriteStderr() don't execute
   indirectly Python signal handlers anymore because mywrite() ignores
   exceptions (KeyboardInterrupt)
@@ -282,6 +285,9 @@
 C-API
 -----
 
+- Add PyArg_ValidateKeywordArguments, which checks if all keyword arguments are
+  strings in an efficient manner.
+
 - Issue #8276: PyEval_CallObject() is now only available in macro form.  The
   function declaration, which was kept for backwards compatibility reasons,
   is now removed (the macro was introduced in 1997!).

Modified: python/branches/py3k/Objects/dictobject.c
==============================================================================
--- python/branches/py3k/Objects/dictobject.c	(original)
+++ python/branches/py3k/Objects/dictobject.c	Sat Apr 24 20:21:17 2010
@@ -458,6 +458,21 @@
 	return 0;
 }
 
+int
+_PyDict_HasOnlyStringKeys(PyObject *dict)
+{
+	Py_ssize_t pos = 0;
+	PyObject *key, *value;
+	assert(PyDict_CheckExact(dict));
+	/* Shortcut */
+	if (((PyDictObject *)dict)->ma_lookup == lookdict_unicode)
+		return 1;
+	while (PyDict_Next(dict, &pos, &key, &value))
+		if (!PyUnicode_Check(key))
+			return 0;
+	return 1;
+}
+
 #ifdef SHOW_TRACK_COUNT
 #define INCREASE_TRACK_COUNT \
 	(count_tracked++, count_untracked--);
@@ -1386,8 +1401,12 @@
 		else
 			result = PyDict_MergeFromSeq2(self, arg, 1);
 	}
-	if (result == 0 && kwds != NULL)
-		result = PyDict_Merge(self, kwds, 1);
+	if (result == 0 && kwds != NULL) {
+		if (PyArg_ValidateKeywordArguments(kwds))
+			result = PyDict_Merge(self, kwds, 1);
+		else
+			result = -1;
+	}
 	return result;
 }
 

Modified: python/branches/py3k/Python/getargs.c
==============================================================================
--- python/branches/py3k/Python/getargs.c	(original)
+++ python/branches/py3k/Python/getargs.c	Sat Apr 24 20:21:17 2010
@@ -1607,6 +1607,21 @@
 	return retval;
 }
 
+int
+PyArg_ValidateKeywordArguments(PyObject *kwargs)
+{
+	if (!PyDict_CheckExact(kwargs)) {
+		PyErr_BadInternalCall();
+		return 0;
+	}
+	if (!_PyDict_HasOnlyStringKeys(kwargs)) {
+		PyErr_SetString(PyExc_TypeError,
+				"keyword arguments must be strings");
+		return 0;
+	}
+	return 1;
+}
+
 #define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':')
 
 static int


More information about the Python-checkins mailing list