Skip to content

Commit e2c88bd

Browse files
orenmnserhiy-storchaka
authored andcommitted
bpo-28298: make array 'Q', 'L' and 'I' accept big intables as elements
1 parent 5fad493 commit e2c88bd

File tree

3 files changed

+88
-48
lines changed

3 files changed

+88
-48
lines changed

Lib/test/test_array.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,26 @@ def test_frombytearray(self):
12251225
b = array.array(self.typecode, a)
12261226
self.assertEqual(a, b)
12271227

1228-
class SignedNumberTest(NumberTest):
1228+
class IntegerNumberTest(NumberTest):
1229+
def test_type_error(self):
1230+
a = array.array(self.typecode)
1231+
a.append(42)
1232+
with self.assertRaises(TypeError):
1233+
a.append(42.0)
1234+
with self.assertRaises(TypeError):
1235+
a[0] = 42.0
1236+
1237+
class Intable:
1238+
def __init__(self, num):
1239+
self._num = num
1240+
def __int__(self):
1241+
return self._num
1242+
def __sub__(self, other):
1243+
return Intable(int(self) - int(other))
1244+
def __add__(self, other):
1245+
return Intable(int(self) + int(other))
1246+
1247+
class SignedNumberTest(IntegerNumberTest):
12291248
example = [-1, 0, 1, 42, 0x7f]
12301249
smallerexample = [-1, 0, 1, 42, 0x7e]
12311250
biggerexample = [-1, 0, 1, 43, 0x7f]
@@ -1236,8 +1255,9 @@ def test_overflow(self):
12361255
lower = -1 * int(pow(2, a.itemsize * 8 - 1))
12371256
upper = int(pow(2, a.itemsize * 8 - 1)) - 1
12381257
self.check_overflow(lower, upper)
1258+
self.check_overflow(Intable(lower), Intable(upper))
12391259

1240-
class UnsignedNumberTest(NumberTest):
1260+
class UnsignedNumberTest(IntegerNumberTest):
12411261
example = [0, 1, 17, 23, 42, 0xff]
12421262
smallerexample = [0, 1, 17, 23, 42, 0xfe]
12431263
biggerexample = [0, 1, 17, 23, 43, 0xff]
@@ -1248,6 +1268,7 @@ def test_overflow(self):
12481268
lower = 0
12491269
upper = int(pow(2, a.itemsize * 8)) - 1
12501270
self.check_overflow(lower, upper)
1271+
self.check_overflow(Intable(lower), Intable(upper))
12511272

12521273
def test_bytes_extend(self):
12531274
s = bytes(self.example)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ Extension Modules
4141
Library
4242
-------
4343

44+
- bpo-28298: Fix a bug that prevented array 'Q', 'L' and 'I' from accepting big
45+
intables (objects that have __int__) as elements. Patch by Oren Milman.
46+
4447
- bpo-29615: SimpleXMLRPCDispatcher no longer chains KeyError (or any other
4548
exception) to exception(s) raised in the dispatched methods.
4649
Patch by Petr Motejlek.

Modules/arraymodule.c

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -331,35 +331,51 @@ II_getitem(arrayobject *ap, Py_ssize_t i)
331331
(unsigned long) ((unsigned int *)ap->ob_item)[i]);
332332
}
333333

334+
static PyObject *
335+
get_int_unless_float(PyObject *v)
336+
{
337+
if (PyFloat_Check(v)) {
338+
PyErr_SetString(PyExc_TypeError,
339+
"array item must be integer");
340+
return NULL;
341+
}
342+
return (PyObject *)_PyLong_FromNbInt(v);
343+
}
344+
334345
static int
335346
II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
336347
{
337348
unsigned long x;
338-
if (PyLong_Check(v)) {
339-
x = PyLong_AsUnsignedLong(v);
340-
if (x == (unsigned long) -1 && PyErr_Occurred())
349+
int do_decref = 0; /* if nb_int was called */
350+
351+
if (!PyLong_Check(v)) {
352+
v = get_int_unless_float(v);
353+
if (NULL == v) {
341354
return -1;
355+
}
356+
do_decref = 1;
342357
}
343-
else {
344-
long y;
345-
if (!PyArg_Parse(v, "l;array item must be integer", &y))
346-
return -1;
347-
if (y < 0) {
348-
PyErr_SetString(PyExc_OverflowError,
349-
"unsigned int is less than minimum");
350-
return -1;
358+
x = PyLong_AsUnsignedLong(v);
359+
if (x == (unsigned long)-1 && PyErr_Occurred()) {
360+
if (do_decref) {
361+
Py_DECREF(v);
351362
}
352-
x = (unsigned long)y;
353-
363+
return -1;
354364
}
355365
if (x > UINT_MAX) {
356366
PyErr_SetString(PyExc_OverflowError,
357-
"unsigned int is greater than maximum");
367+
"unsigned int is greater than maximum");
368+
if (do_decref) {
369+
Py_DECREF(v);
370+
}
358371
return -1;
359372
}
360-
361373
if (i >= 0)
362374
((unsigned int *)ap->ob_item)[i] = (unsigned int)x;
375+
376+
if (do_decref) {
377+
Py_DECREF(v);
378+
}
363379
return 0;
364380
}
365381

@@ -390,31 +406,28 @@ static int
390406
LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
391407
{
392408
unsigned long x;
393-
if (PyLong_Check(v)) {
394-
x = PyLong_AsUnsignedLong(v);
395-
if (x == (unsigned long) -1 && PyErr_Occurred())
396-
return -1;
397-
}
398-
else {
399-
long y;
400-
if (!PyArg_Parse(v, "l;array item must be integer", &y))
401-
return -1;
402-
if (y < 0) {
403-
PyErr_SetString(PyExc_OverflowError,
404-
"unsigned long is less than minimum");
409+
int do_decref = 0; /* if nb_int was called */
410+
411+
if (!PyLong_Check(v)) {
412+
v = get_int_unless_float(v);
413+
if (NULL == v) {
405414
return -1;
406415
}
407-
x = (unsigned long)y;
408-
416+
do_decref = 1;
409417
}
410-
if (x > ULONG_MAX) {
411-
PyErr_SetString(PyExc_OverflowError,
412-
"unsigned long is greater than maximum");
418+
x = PyLong_AsUnsignedLong(v);
419+
if (x == (unsigned long)-1 && PyErr_Occurred()) {
420+
if (do_decref) {
421+
Py_DECREF(v);
422+
}
413423
return -1;
414424
}
415-
416425
if (i >= 0)
417426
((unsigned long *)ap->ob_item)[i] = x;
427+
428+
if (do_decref) {
429+
Py_DECREF(v);
430+
}
418431
return 0;
419432
}
420433

@@ -448,25 +461,28 @@ static int
448461
QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
449462
{
450463
unsigned PY_LONG_LONG x;
451-
if (PyLong_Check(v)) {
452-
x = PyLong_AsUnsignedLongLong(v);
453-
if (x == (unsigned PY_LONG_LONG) -1 && PyErr_Occurred())
464+
int do_decref = 0; /* if nb_int was called */
465+
466+
if (!PyLong_Check(v)) {
467+
v = get_int_unless_float(v);
468+
if (NULL == v) {
454469
return -1;
470+
}
471+
do_decref = 1;
455472
}
456-
else {
457-
PY_LONG_LONG y;
458-
if (!PyArg_Parse(v, "L;array item must be integer", &y))
459-
return -1;
460-
if (y < 0) {
461-
PyErr_SetString(PyExc_OverflowError,
462-
"unsigned long long is less than minimum");
463-
return -1;
473+
x = PyLong_AsUnsignedLongLong(v);
474+
if (x == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) {
475+
if (do_decref) {
476+
Py_DECREF(v);
464477
}
465-
x = (unsigned PY_LONG_LONG)y;
478+
return -1;
466479
}
467-
468480
if (i >= 0)
469481
((unsigned PY_LONG_LONG *)ap->ob_item)[i] = x;
482+
483+
if (do_decref) {
484+
Py_DECREF(v);
485+
}
470486
return 0;
471487
}
472488
#endif

0 commit comments

Comments
 (0)