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.
This commit is contained in:
Benjamin Peterson 2010-04-24 18:21:17 +00:00
parent b962171414
commit fb88636199
7 changed files with 57 additions and 2 deletions

View File

@ -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, ...)

View File

@ -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);

View File

@ -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 *, ...);

View File

@ -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(), {})

View File

@ -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!).

View File

@ -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)
if (result == 0 && kwds != NULL) {
if (PyArg_ValidateKeywordArguments(kwds))
result = PyDict_Merge(self, kwds, 1);
else
result = -1;
}
return result;
}

View File

@ -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