mirror of
https://github.com/python/cpython.git
synced 2025-11-01 02:38:53 +00:00
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:
parent
b962171414
commit
fb88636199
7 changed files with 57 additions and 2 deletions
|
|
@ -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);
|
||||||
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 *, ...);
|
||||||
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
|
||||||
|
|
||||||
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?
|
||||||
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
|
||||||
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)
|
||||||
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
|
||||||
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,
|
||||||
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue