diff options
author | Benjamin Peterson <benjamin@python.org> | 2010-04-24 18:21:17 (GMT) |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2010-04-24 18:21:17 (GMT) |
commit | fb88636199c12f63d6c8c89f311cdafc91f30d2f (patch) | |
tree | 24b2758c45ac543fe9970f861ca091852a8f4a94 | |
parent | b962171414aa2cfe3dbfbd4294819a4153a7bd6c (diff) | |
download | cpython-fb88636199c12f63d6c8c89f311cdafc91f30d2f.zip cpython-fb88636199c12f63d6c8c89f311cdafc91f30d2f.tar.gz cpython-fb88636199c12f63d6c8c89f311cdafc91f30d2f.tar.bz2 |
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.
-rw-r--r-- | Doc/c-api/arg.rst | 7 | ||||
-rw-r--r-- | Include/dictobject.h | 1 | ||||
-rw-r--r-- | Include/modsupport.h | 1 | ||||
-rw-r--r-- | Lib/test/test_dict.py | 6 | ||||
-rw-r--r-- | Misc/NEWS | 6 | ||||
-rw-r--r-- | Objects/dictobject.c | 23 | ||||
-rw-r--r-- | Python/getargs.c | 15 |
7 files changed, 57 insertions, 2 deletions
diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index fc4b941..a4e5555 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -366,6 +366,13 @@ and the following format units are left untouched. 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, ...) diff --git a/Include/dictobject.h b/Include/dictobject.h index d8c409e..5623379 100644 --- a/Include/dictobject.h +++ b/Include/dictobject.h @@ -126,6 +126,7 @@ PyAPI_FUNC(int) PyDict_Contains(PyObject *mp, PyObject *key); 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); diff --git a/Include/modsupport.h b/Include/modsupport.h index 23e8fa6..57886df 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -27,6 +27,7 @@ PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...); 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 *, ...); diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 4ba0b71..6c5f682 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -7,6 +7,12 @@ import gc, weakref 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(), {}) @@ -12,6 +12,9 @@ What's New in Python 3.2 Alpha 1? 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 @@ Core and Builtins 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!). diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 1d36e1d..5433ff7 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -458,6 +458,21 @@ lookdict_unicode(PyDictObject *mp, PyObject *key, register long hash) 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 @@ dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methnam 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; } diff --git a/Python/getargs.c b/Python/getargs.c index 17c5317..69f5018 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1607,6 +1607,21 @@ _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *args, 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 |