From 1a7aab70d1f56b63ba6798db1cafe0a61cc3da15 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 4 Jan 2001 22:33:02 +0000 Subject: [PATCH] When a PyCFunction that takes only positional parameters is called with an empty keywords dictionary (via apply() or the extended call syntax), the keywords dict should be ignored. If the keywords dict is not empty, TypeError should be raised. (Between the restructuring of the call machinery and this patch, an empty dict in this situation would trigger a SystemError via PyErr_BadInternalCall().) Added regression tests to detect errors for this. --- Lib/test/test_b1.py | 11 ++++++++++ Lib/test/test_extcall.py | 12 ++++++++++- Python/ceval.c | 43 ++++++++++++++++++++-------------------- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_b1.py b/Lib/test/test_b1.py index 6d6aa6fcf18..95110790f0a 100644 --- a/Lib/test/test_b1.py +++ b/Lib/test/test_b1.py @@ -39,6 +39,17 @@ def f3(a1, a2, a3): apply(f2, (1, 2)) apply(f3, (1, 2, 3)) +# A PyCFunction that takes only positional parameters should allow an +# empty keyword dictionary to pass without a complaint, but raise a +# TypeError if the dictionary is non-empty. +apply(id, (1,), {}) +try: + apply(id, (1,), {"foo": 1}) +except TypeError: + pass +else: + raise TestFailed, 'expected TypeError; no exception raised' + print 'callable' if not callable(len):raise TestFailed, 'callable(len)' def f(): pass diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 7dddabca65c..cc42818c9a6 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -1,4 +1,5 @@ from UserList import UserList +from test_support import TestFailed def f(*a, **k): print a, k @@ -161,4 +162,13 @@ def method(self, arg1, arg2): except TypeError, err: print err - +# A PyCFunction that takes only positional parameters should allow an +# empty keyword dictionary to pass without a complaint, but raise a +# TypeError if the dictionary is non-empty. +id(1, **{}) +try: + id(1, **{"foo": 1}) +except TypeError: + pass +else: + raise TestFailed, 'expected TypeError; no exception raised' diff --git a/Python/ceval.c b/Python/ceval.c index dd626e5f88d..1559456e6bf 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2607,36 +2607,37 @@ call_cfunction(PyObject *func, PyObject *arg, PyObject *kw) PyObject *self = PyCFunction_GET_SELF(func); int flags = PyCFunction_GET_FLAGS(func); - if (flags & METH_KEYWORDS && kw == NULL) { - static PyObject *dict = NULL; - if (dict == NULL) { - dict = PyDict_New(); - if (dict == NULL) - return NULL; - } - kw = dict; - Py_INCREF(dict); - } - if (flags & METH_VARARGS && kw == NULL) { - return (*meth)(self, arg); - } if (flags & METH_KEYWORDS) { + if (kw == NULL) { + static PyObject *dict = NULL; + if (dict == NULL) { + dict = PyDict_New(); + if (dict == NULL) + return NULL; + } + kw = dict; + Py_INCREF(dict); + } return (*(PyCFunctionWithKeywords)meth)(self, arg, kw); } - if (!(flags & METH_VARARGS)) { - int size = PyTuple_GET_SIZE(arg); - if (size == 1) - arg = PyTuple_GET_ITEM(arg, 0); - else if (size == 0) - arg = NULL; - return (*meth)(self, arg); - } if (kw != NULL && PyDict_Size(kw) != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", f->m_ml->ml_name); return NULL; } + if (flags & METH_VARARGS) { + return (*meth)(self, arg); + } + if (!(flags & METH_VARARGS)) { + /* the really old style */ + int size = PyTuple_GET_SIZE(arg); + if (size == 1) + arg = PyTuple_GET_ITEM(arg, 0); + else if (size == 0) + arg = NULL; + return (*meth)(self, arg); + } /* should never get here ??? */ PyErr_BadInternalCall(); return NULL;