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.
This commit is contained in:
Jeremy Hylton 2000-03-30 23:55:31 +00:00
parent aaf0ab26ed
commit 074c3e62d1
3 changed files with 69 additions and 12 deletions

View File

@ -16,6 +16,10 @@ TypeError: not enough arguments; expected 1, got 0
1 (2,) {} 1 (2,) {}
1 (2, 3) {} 1 (2, 3) {}
1 (2, 3, 4, 5) {} 1 (2, 3, 4, 5) {}
0 (1, 2) {}
1 () {'d': 4, 'b': 2, 'c': 3, 'a': 1}
{'b': 2, 'c': 3, 'a': 1}
{'b': 2, 'c': 3, 'a': 1}
keyword parameter redefined: x keyword parameter redefined: x
keyword parameter redefined: b keyword parameter redefined: b
keywords must be strings keywords must be strings

View File

@ -46,6 +46,50 @@ g(1)
g(1, 2) g(1, 2)
g(1, 2, 3) g(1, 2, 3)
g(1, 2, 3, *(4, 5)) g(1, 2, 3, *(4, 5))
class Nothing: pass
try:
g(*Nothing())
except AttributeError, attr:
assert attr[0] == '__len__'
else:
print "should raise AttributeError: __len__"
class Nothing:
def __len__(self):
return 5
try:
g(*Nothing())
except AttributeError, attr:
assert attr[0] == '__getitem__'
else:
print "should raise AttributeError: __getitem__"
class Nothing:
def __len__(self):
return 5
def __getitem__(self, i):
if i < 3:
return i
else:
raise IndexError, i
g(*Nothing())
# make sure the function call doesn't stomp on the dictionary?
d = {'a': 1, 'b': 2, 'c': 3}
d2 = d.copy()
assert d == d2
g(1, d=4, **d)
print d
print d2
assert d == d2, "function call modified dictionary"
# what about willful misconduct?
def saboteur(**kw):
kw['x'] = locals()
d = {}
saboteur(a=1, **d)
assert d == {}
try: try:
g(1, 2, 3, **{'x':4, 'y':5}) g(1, 2, 3, **{'x':4, 'y':5})
except TypeError, err: except TypeError, err:

View File

@ -1635,7 +1635,18 @@ eval_code2(co, globals, locals,
x = NULL; x = NULL;
break; break;
} }
nstar = PySequence_Length(stararg); /* Convert abstract sequence to concrete tuple */
if (!PyTuple_Check(stararg)) {
PyObject *t = NULL;
t = PySequence_Tuple(stararg);
if (t == NULL) {
x = NULL;
break;
}
Py_DECREF(stararg);
stararg = t;
}
nstar = PyTuple_GET_SIZE(stararg);
if (nstar < 0) { if (nstar < 0) {
x = NULL; x = NULL;
break; break;
@ -1649,6 +1660,15 @@ eval_code2(co, globals, locals,
break; break;
} }
} }
else {
PyObject *d = PyDict_Copy(kwdict);
if (d == NULL) {
x = NULL;
break;
}
Py_DECREF(kwdict);
kwdict = d;
}
err = 0; err = 0;
while (--nk >= 0) { while (--nk >= 0) {
PyObject *value = POP(); PyObject *value = POP();
@ -1678,18 +1698,7 @@ eval_code2(co, globals, locals,
break; break;
} }
if (stararg) { if (stararg) {
PyObject *t = NULL;
int i; int i;
if (!PyTuple_Check(stararg)) {
/* must be sequence to pass earlier test */
t = PySequence_Tuple(stararg);
if (t == NULL) {
x = NULL;
break;
}
Py_DECREF(stararg);
stararg = t;
}
for (i = 0; i < nstar; i++) { for (i = 0; i < nstar; i++) {
PyObject *a = PyTuple_GET_ITEM(stararg, i); PyObject *a = PyTuple_GET_ITEM(stararg, i);
Py_INCREF(a); Py_INCREF(a);