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

Skip to content

Commit 074c3e6

Browse files
committed
Two fixes for extended call syntax:
If a non-tuple sequence is passed as the *arg, convert it to a tuple before checking its length. If named keyword arguments are used in combination with **kwargs, make a copy of kwargs before inserting the new keys.
1 parent aaf0ab2 commit 074c3e6

3 files changed

Lines changed: 69 additions & 12 deletions

File tree

Lib/test/output/test_extcall

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ TypeError: not enough arguments; expected 1, got 0
1616
1 (2,) {}
1717
1 (2, 3) {}
1818
1 (2, 3, 4, 5) {}
19+
0 (1, 2) {}
20+
1 () {'d': 4, 'b': 2, 'c': 3, 'a': 1}
21+
{'b': 2, 'c': 3, 'a': 1}
22+
{'b': 2, 'c': 3, 'a': 1}
1923
keyword parameter redefined: x
2024
keyword parameter redefined: b
2125
keywords must be strings

Lib/test/test_extcall.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,50 @@ def h(j=1, a=2, h=3):
4646
g(1, 2)
4747
g(1, 2, 3)
4848
g(1, 2, 3, *(4, 5))
49+
class Nothing: pass
50+
try:
51+
g(*Nothing())
52+
except AttributeError, attr:
53+
assert attr[0] == '__len__'
54+
else:
55+
print "should raise AttributeError: __len__"
56+
57+
class Nothing:
58+
def __len__(self):
59+
return 5
60+
try:
61+
g(*Nothing())
62+
except AttributeError, attr:
63+
assert attr[0] == '__getitem__'
64+
else:
65+
print "should raise AttributeError: __getitem__"
66+
67+
class Nothing:
68+
def __len__(self):
69+
return 5
70+
def __getitem__(self, i):
71+
if i < 3:
72+
return i
73+
else:
74+
raise IndexError, i
75+
g(*Nothing())
76+
77+
# make sure the function call doesn't stomp on the dictionary?
78+
d = {'a': 1, 'b': 2, 'c': 3}
79+
d2 = d.copy()
80+
assert d == d2
81+
g(1, d=4, **d)
82+
print d
83+
print d2
84+
assert d == d2, "function call modified dictionary"
85+
86+
# what about willful misconduct?
87+
def saboteur(**kw):
88+
kw['x'] = locals()
89+
d = {}
90+
saboteur(a=1, **d)
91+
assert d == {}
92+
4993
try:
5094
g(1, 2, 3, **{'x':4, 'y':5})
5195
except TypeError, err:

Python/ceval.c

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,18 @@ eval_code2(co, globals, locals,
16351635
x = NULL;
16361636
break;
16371637
}
1638-
nstar = PySequence_Length(stararg);
1638+
/* Convert abstract sequence to concrete tuple */
1639+
if (!PyTuple_Check(stararg)) {
1640+
PyObject *t = NULL;
1641+
t = PySequence_Tuple(stararg);
1642+
if (t == NULL) {
1643+
x = NULL;
1644+
break;
1645+
}
1646+
Py_DECREF(stararg);
1647+
stararg = t;
1648+
}
1649+
nstar = PyTuple_GET_SIZE(stararg);
16391650
if (nstar < 0) {
16401651
x = NULL;
16411652
break;
@@ -1649,6 +1660,15 @@ eval_code2(co, globals, locals,
16491660
break;
16501661
}
16511662
}
1663+
else {
1664+
PyObject *d = PyDict_Copy(kwdict);
1665+
if (d == NULL) {
1666+
x = NULL;
1667+
break;
1668+
}
1669+
Py_DECREF(kwdict);
1670+
kwdict = d;
1671+
}
16521672
err = 0;
16531673
while (--nk >= 0) {
16541674
PyObject *value = POP();
@@ -1678,18 +1698,7 @@ eval_code2(co, globals, locals,
16781698
break;
16791699
}
16801700
if (stararg) {
1681-
PyObject *t = NULL;
16821701
int i;
1683-
if (!PyTuple_Check(stararg)) {
1684-
/* must be sequence to pass earlier test */
1685-
t = PySequence_Tuple(stararg);
1686-
if (t == NULL) {
1687-
x = NULL;
1688-
break;
1689-
}
1690-
Py_DECREF(stararg);
1691-
stararg = t;
1692-
}
16931702
for (i = 0; i < nstar; i++) {
16941703
PyObject *a = PyTuple_GET_ITEM(stararg, i);
16951704
Py_INCREF(a);

0 commit comments

Comments
 (0)