NULL = None
+class CustomError(Exception):
+ pass
+
class Index:
def __index__(self):
return 99
ret = getargs_tuple(1, (2, 3))
self.assertEqual(ret, (1,2,3))
- # make sure invalid tuple arguments are handled correctly
- class seq:
+ # make sure invalid sequence arguments are handled correctly
+ class TestSeq:
def __len__(self):
return 2
def __getitem__(self, n):
- raise ValueError
- self.assertRaises(TypeError, getargs_tuple, 1, seq())
+ raise CustomError
+ self.assertRaises(CustomError, getargs_tuple, 1, TestSeq())
class Keywords_TestCase(unittest.TestCase):
def test_kwargs(self):
f"this function got an unexpected keyword argument '{name2}'"):
parse((), {name2: 1, name3: 2}, '|OO', [name, name3])
- def test_nested_tuple(self):
+ def test_nested_sequence(self):
parse = _testcapi.parse_tuple_and_keywords
self.assertEqual(parse(((1, 2, 3),), {}, '(OOO)', ['a']), (1, 2, 3))
self.assertEqual(parse((1, (2, 3), 4), {}, 'O(OO)O', ['a', 'b', 'c']),
(1, 2, 3, 4))
parse(((1, 2, 3),), {}, '(iii)', ['a'])
+ parse(([1, 2, 3],), {}, '(iii)', ['a'])
with self.assertRaisesRegex(TypeError,
- "argument 1 must be sequence of length 2, not 3"):
+ "argument 1 must be tuple of length 2, not 3"):
parse(((1, 2, 3),), {}, '(ii)', ['a'])
with self.assertRaisesRegex(TypeError,
- "argument 1 must be sequence of length 2, not 1"):
+ "argument 1 must be tuple of length 2, not 1"):
parse(((1,),), {}, '(ii)', ['a'])
with self.assertRaisesRegex(TypeError,
- "argument 1 must be 2-item sequence, not int"):
+ "argument 1 must be sequence of length 2, not 3"):
+ parse(([1, 2, 3],), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be sequence of length 2, not 1"):
+ parse(([1,],), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be 2-item tuple, not int"):
parse((1,), {}, '(ii)', ['a'])
with self.assertRaisesRegex(TypeError,
- "argument 1 must be 2-item sequence, not bytes"):
+ "argument 1 must be 2-item tuple, not None$"):
+ parse((None,), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be 2-item tuple, not str"):
+ parse(('ab',), {}, '(CC)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be 2-item tuple, not bytes"):
parse((b'ab',), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be 2-item tuple, not bytearray"):
+ parse((bytearray(b'ab'),), {}, '(ii)', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be 2-item tuple, not dict"):
+ parse(({},), {}, '(ii)', ['a'])
+
+ with self.assertWarnsRegex(DeprecationWarning,
+ "argument must be 3-item tuple, not list"):
+ self.assertEqual(parse(([1, 2, 3],), {}, '(OOO)', ['a']), (1, 2, 3))
+ with self.assertWarnsRegex(DeprecationWarning,
+ "argument must be 2-item tuple, not list"):
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be tuple of length 2, not 3"):
+ parse(([1, 2, 3],), {}, '(OO)', ['a'])
+ with self.assertWarnsRegex(DeprecationWarning,
+ "argument must be 2-item tuple, not list"):
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be tuple of length 2, not 1"):
+ parse(([1,],), {}, '(OO)', ['a'])
for f in 'es', 'et', 'es#', 'et#':
with self.assertRaises(LookupError): # empty encoding ""
parse((('a',),), {}, '(' + f + ')', ['a'])
with self.assertRaisesRegex(TypeError,
- "argument 1 must be sequence of length 1, not 0"):
+ "argument 1 must be tuple of length 1, not 0"):
parse(((),), {}, '(' + f + ')', ['a'])
+ with self.assertRaisesRegex(TypeError,
+ "argument 1 must be sequence of length 1, not 0"):
+ parse(([],), {}, '(' + f + ')', ['a'])
@unittest.skipIf(_testinternalcapi is None, 'needs _testinternalcapi')
def test_gh_119213(self):
const char *format = *p_format;
int i;
Py_ssize_t len;
+ int istuple = PyTuple_Check(arg);
+ int mustbetuple = istuple;
for (;;) {
int c = *format++;
}
else if (c == ':' || c == ';' || c == '\0')
break;
- else if (level == 0 && Py_ISALPHA(c) && c != 'e')
- n++;
+ else {
+ if (level == 0 && Py_ISALPHA(c)) {
+ n++;
+ }
+ if (c == 'e' && (*format == 's' || *format == 't')) {
+ format++;
+ continue;
+ }
+ if (!mustbetuple) {
+ switch (c) {
+ case 'y':
+ case 's':
+ case 'z':
+ if (*format != '*') {
+ mustbetuple = 1;
+ }
+ break;
+ case 'S':
+ case 'Y':
+ case 'U':
+ mustbetuple = 1;
+ break;
+ case 'O':
+ if (*format != '&') {
+ mustbetuple = 1;
+ }
+ break;
+ }
+ }
+ }
}
- if (!PySequence_Check(arg) || PyBytes_Check(arg)) {
+ if (istuple) {
+ /* fallthrough */
+ }
+ else if (!PySequence_Check(arg) ||
+ PyUnicode_Check(arg) || PyBytes_Check(arg) || PyByteArray_Check(arg))
+ {
levels[0] = 0;
PyOS_snprintf(msgbuf, bufsize,
- "must be %d-item sequence, not %.50s",
+ "must be %d-item tuple, not %.50s",
n,
arg == Py_None ? "None" : Py_TYPE(arg)->tp_name);
return msgbuf;
}
+ else {
+ if (mustbetuple) {
+ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 0,
+ "argument must be %d-item tuple, not %T", n, arg))
+ {
+ return msgbuf;
+ }
+ }
+ len = PySequence_Size(arg);
+ if (len != n) {
+ levels[0] = 0;
+ PyOS_snprintf(msgbuf, bufsize,
+ "must be %s of length %d, not %zd",
+ mustbetuple ? "tuple" : "sequence", n, len);
+ return msgbuf;
+ }
+ arg = PySequence_Tuple(arg);
+ if (arg == NULL) {
+ return msgbuf;
+ }
+ }
- len = PySequence_Size(arg);
+ len = PyTuple_GET_SIZE(arg);
if (len != n) {
levels[0] = 0;
PyOS_snprintf(msgbuf, bufsize,
- "must be sequence of length %d, not %zd",
+ "must be tuple of length %d, not %zd",
n, len);
+ if (!istuple) {
+ Py_DECREF(arg);
+ }
return msgbuf;
}
format = *p_format;
for (i = 0; i < n; i++) {
const char *msg;
- PyObject *item;
- item = PySequence_GetItem(arg, i);
- if (item == NULL) {
- PyErr_Clear();
- levels[0] = i+1;
- levels[1] = 0;
- strncpy(msgbuf, "is not retrievable", bufsize);
- return msgbuf;
- }
+ PyObject *item = PyTuple_GET_ITEM(arg, i);
msg = convertitem(item, &format, p_va, flags, levels+1,
msgbuf, bufsize, freelist);
- /* PySequence_GetItem calls tp->sq_item, which INCREFs */
- Py_XDECREF(item);
if (msg != NULL) {
levels[0] = i+1;
+ if (!istuple) {
+ Py_DECREF(arg);
+ }
return msg;
}
}
*p_format = format;
+ if (!istuple) {
+ Py_DECREF(arg);
+ }
return NULL;
}