summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2010-04-24 18:21:17 (GMT)
committerBenjamin Peterson <benjamin@python.org>2010-04-24 18:21:17 (GMT)
commitfb88636199c12f63d6c8c89f311cdafc91f30d2f (patch)
tree24b2758c45ac543fe9970f861ca091852a8f4a94
parentb962171414aa2cfe3dbfbd4294819a4153a7bd6c (diff)
downloadcpython-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.rst7
-rw-r--r--Include/dictobject.h1
-rw-r--r--Include/modsupport.h1
-rw-r--r--Lib/test/test_dict.py6
-rw-r--r--Misc/NEWS6
-rw-r--r--Objects/dictobject.c23
-rw-r--r--Python/getargs.c15
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(), {})
diff --git a/Misc/NEWS b/Misc/NEWS
index 44e7fa2..0d72c83 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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