Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1a7aab7

Browse files
committed
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.
1 parent be4c0f5 commit 1a7aab7

3 files changed

Lines changed: 41 additions & 19 deletions

File tree

Lib/test/test_b1.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ def f3(a1, a2, a3):
3939
apply(f2, (1, 2))
4040
apply(f3, (1, 2, 3))
4141

42+
# A PyCFunction that takes only positional parameters should allow an
43+
# empty keyword dictionary to pass without a complaint, but raise a
44+
# TypeError if the dictionary is non-empty.
45+
apply(id, (1,), {})
46+
try:
47+
apply(id, (1,), {"foo": 1})
48+
except TypeError:
49+
pass
50+
else:
51+
raise TestFailed, 'expected TypeError; no exception raised'
52+
4253
print 'callable'
4354
if not callable(len):raise TestFailed, 'callable(len)'
4455
def f(): pass

Lib/test/test_extcall.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from UserList import UserList
2+
from test_support import TestFailed
23

34
def f(*a, **k):
45
print a, k
@@ -161,4 +162,13 @@ def method(self, arg1, arg2):
161162
except TypeError, err:
162163
print err
163164

164-
165+
# A PyCFunction that takes only positional parameters should allow an
166+
# empty keyword dictionary to pass without a complaint, but raise a
167+
# TypeError if the dictionary is non-empty.
168+
id(1, **{})
169+
try:
170+
id(1, **{"foo": 1})
171+
except TypeError:
172+
pass
173+
else:
174+
raise TestFailed, 'expected TypeError; no exception raised'

Python/ceval.c

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2607,36 +2607,37 @@ call_cfunction(PyObject *func, PyObject *arg, PyObject *kw)
26072607
PyObject *self = PyCFunction_GET_SELF(func);
26082608
int flags = PyCFunction_GET_FLAGS(func);
26092609

2610-
if (flags & METH_KEYWORDS && kw == NULL) {
2611-
static PyObject *dict = NULL;
2612-
if (dict == NULL) {
2613-
dict = PyDict_New();
2614-
if (dict == NULL)
2615-
return NULL;
2610+
if (flags & METH_KEYWORDS) {
2611+
if (kw == NULL) {
2612+
static PyObject *dict = NULL;
2613+
if (dict == NULL) {
2614+
dict = PyDict_New();
2615+
if (dict == NULL)
2616+
return NULL;
2617+
}
2618+
kw = dict;
2619+
Py_INCREF(dict);
26162620
}
2617-
kw = dict;
2618-
Py_INCREF(dict);
2621+
return (*(PyCFunctionWithKeywords)meth)(self, arg, kw);
26192622
}
2620-
if (flags & METH_VARARGS && kw == NULL) {
2621-
return (*meth)(self, arg);
2623+
if (kw != NULL && PyDict_Size(kw) != 0) {
2624+
PyErr_Format(PyExc_TypeError,
2625+
"%.200s() takes no keyword arguments",
2626+
f->m_ml->ml_name);
2627+
return NULL;
26222628
}
2623-
if (flags & METH_KEYWORDS) {
2624-
return (*(PyCFunctionWithKeywords)meth)(self, arg, kw);
2629+
if (flags & METH_VARARGS) {
2630+
return (*meth)(self, arg);
26252631
}
26262632
if (!(flags & METH_VARARGS)) {
2633+
/* the really old style */
26272634
int size = PyTuple_GET_SIZE(arg);
26282635
if (size == 1)
26292636
arg = PyTuple_GET_ITEM(arg, 0);
26302637
else if (size == 0)
26312638
arg = NULL;
26322639
return (*meth)(self, arg);
26332640
}
2634-
if (kw != NULL && PyDict_Size(kw) != 0) {
2635-
PyErr_Format(PyExc_TypeError,
2636-
"%.200s() takes no keyword arguments",
2637-
f->m_ml->ml_name);
2638-
return NULL;
2639-
}
26402641
/* should never get here ??? */
26412642
PyErr_BadInternalCall();
26422643
return NULL;

0 commit comments

Comments
 (0)