Kaydet (Commit) fb886361 authored tarafından Benjamin Peterson's avatar Benjamin Peterson

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.
üst b9621714
...@@ -366,6 +366,13 @@ and the following format units are left untouched. ...@@ -366,6 +366,13 @@ and the following format units are left untouched.
va_list rather than a variable number of arguments. 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 .. XXX deprecated, will be removed
.. cfunction:: int PyArg_Parse(PyObject *args, const char *format, ...) .. cfunction:: int PyArg_Parse(PyObject *args, const char *format, ...)
......
...@@ -126,6 +126,7 @@ PyAPI_FUNC(int) PyDict_Contains(PyObject *mp, PyObject *key); ...@@ -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(int) _PyDict_Contains(PyObject *mp, PyObject *key, long hash);
PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused); PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp); 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). */ /* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */
PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other); PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other);
......
...@@ -27,6 +27,7 @@ PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...); ...@@ -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_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
const char *, char **, ...); 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(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...);
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...); PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...); PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);
......
...@@ -7,6 +7,12 @@ import gc, weakref ...@@ -7,6 +7,12 @@ import gc, weakref
class DictTest(unittest.TestCase): 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): def test_constructor(self):
# calling built-in types without argument must return empty # calling built-in types without argument must return empty
self.assertEqual(dict(), {}) self.assertEqual(dict(), {})
......
...@@ -12,6 +12,9 @@ What's New in Python 3.2 Alpha 1? ...@@ -12,6 +12,9 @@ What's New in Python 3.2 Alpha 1?
Core and Builtins 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 - Issue #8124: PySys_WriteStdout() and PySys_WriteStderr() don't execute
indirectly Python signal handlers anymore because mywrite() ignores indirectly Python signal handlers anymore because mywrite() ignores
exceptions (KeyboardInterrupt) exceptions (KeyboardInterrupt)
...@@ -282,6 +285,9 @@ Core and Builtins ...@@ -282,6 +285,9 @@ Core and Builtins
C-API 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 - Issue #8276: PyEval_CallObject() is now only available in macro form. The
function declaration, which was kept for backwards compatibility reasons, function declaration, which was kept for backwards compatibility reasons,
is now removed (the macro was introduced in 1997!). is now removed (the macro was introduced in 1997!).
......
...@@ -458,6 +458,21 @@ lookdict_unicode(PyDictObject *mp, PyObject *key, register long hash) ...@@ -458,6 +458,21 @@ lookdict_unicode(PyDictObject *mp, PyObject *key, register long hash)
return 0; 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 #ifdef SHOW_TRACK_COUNT
#define INCREASE_TRACK_COUNT \ #define INCREASE_TRACK_COUNT \
(count_tracked++, count_untracked--); (count_tracked++, count_untracked--);
...@@ -1386,8 +1401,12 @@ dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methnam ...@@ -1386,8 +1401,12 @@ dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methnam
else else
result = PyDict_MergeFromSeq2(self, arg, 1); result = PyDict_MergeFromSeq2(self, arg, 1);
} }
if (result == 0 && kwds != NULL) if (result == 0 && kwds != NULL) {
result = PyDict_Merge(self, kwds, 1); if (PyArg_ValidateKeywordArguments(kwds))
result = PyDict_Merge(self, kwds, 1);
else
result = -1;
}
return result; return result;
} }
......
...@@ -1607,6 +1607,21 @@ _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *args, ...@@ -1607,6 +1607,21 @@ _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *args,
return retval; 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 == ':') #define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':')
static int static int
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment