Skip to content

Commit 26d013e

Browse files
orenmnserhiy-storchaka
authored andcommitted
[3.6] bpo-28298: make array 'Q', 'L' and 'I' accept big intables as elements (#579)
1 parent 9cef253 commit 26d013e

File tree

3 files changed

+89
-61
lines changed

3 files changed

+89
-61
lines changed

Lib/test/test_array.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,6 @@
1414
import array
1515
from array import _array_reconstructor as array_reconstructor
1616

17-
try:
18-
# Try to determine availability of long long independently
19-
# of the array module under test
20-
struct.calcsize('@q')
21-
have_long_long = True
22-
except struct.error:
23-
have_long_long = False
24-
2517
sizeof_wchar = array.array('u').itemsize
2618

2719

@@ -32,9 +24,7 @@ class ArraySubclassWithKwargs(array.array):
3224
def __init__(self, typecode, newarg=None):
3325
array.array.__init__(self)
3426

35-
typecodes = "ubBhHiIlLfd"
36-
if have_long_long:
37-
typecodes += 'qQ'
27+
typecodes = 'ubBhHiIlLfdqQ'
3828

3929
class MiscTest(unittest.TestCase):
4030

@@ -1240,7 +1230,26 @@ def test_frombytearray(self):
12401230
b = array.array(self.typecode, a)
12411231
self.assertEqual(a, b)
12421232

1243-
class SignedNumberTest(NumberTest):
1233+
class IntegerNumberTest(NumberTest):
1234+
def test_type_error(self):
1235+
a = array.array(self.typecode)
1236+
a.append(42)
1237+
with self.assertRaises(TypeError):
1238+
a.append(42.0)
1239+
with self.assertRaises(TypeError):
1240+
a[0] = 42.0
1241+
1242+
class Intable:
1243+
def __init__(self, num):
1244+
self._num = num
1245+
def __int__(self):
1246+
return self._num
1247+
def __sub__(self, other):
1248+
return Intable(int(self) - int(other))
1249+
def __add__(self, other):
1250+
return Intable(int(self) + int(other))
1251+
1252+
class SignedNumberTest(IntegerNumberTest):
12441253
example = [-1, 0, 1, 42, 0x7f]
12451254
smallerexample = [-1, 0, 1, 42, 0x7e]
12461255
biggerexample = [-1, 0, 1, 43, 0x7f]
@@ -1251,8 +1260,9 @@ def test_overflow(self):
12511260
lower = -1 * int(pow(2, a.itemsize * 8 - 1))
12521261
upper = int(pow(2, a.itemsize * 8 - 1)) - 1
12531262
self.check_overflow(lower, upper)
1263+
self.check_overflow(Intable(lower), Intable(upper))
12541264

1255-
class UnsignedNumberTest(NumberTest):
1265+
class UnsignedNumberTest(IntegerNumberTest):
12561266
example = [0, 1, 17, 23, 42, 0xff]
12571267
smallerexample = [0, 1, 17, 23, 42, 0xfe]
12581268
biggerexample = [0, 1, 17, 23, 43, 0xff]
@@ -1263,6 +1273,7 @@ def test_overflow(self):
12631273
lower = 0
12641274
upper = int(pow(2, a.itemsize * 8)) - 1
12651275
self.check_overflow(lower, upper)
1276+
self.check_overflow(Intable(lower), Intable(upper))
12661277

12671278
def test_bytes_extend(self):
12681279
s = bytes(self.example)
@@ -1314,12 +1325,10 @@ class UnsignedLongTest(UnsignedNumberTest, unittest.TestCase):
13141325
typecode = 'L'
13151326
minitemsize = 4
13161327

1317-
@unittest.skipIf(not have_long_long, 'need long long support')
13181328
class LongLongTest(SignedNumberTest, unittest.TestCase):
13191329
typecode = 'q'
13201330
minitemsize = 8
13211331

1322-
@unittest.skipIf(not have_long_long, 'need long long support')
13231332
class UnsignedLongLongTest(UnsignedNumberTest, unittest.TestCase):
13241333
typecode = 'Q'
13251334
minitemsize = 8

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Core and Builtins
1616
Library
1717
-------
1818

19+
- bpo-28298: Fix a bug that prevented array 'Q', 'L' and 'I' from accepting big
20+
intables (objects that have __int__) as elements. Patch by Oren Milman.
21+
1922
- bpo-28231: The zipfile module now accepts path-like objects for external
2023
paths.
2124

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

@@ -446,25 +459,28 @@ static int
446459
QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
447460
{
448461
unsigned long long x;
449-
if (PyLong_Check(v)) {
450-
x = PyLong_AsUnsignedLongLong(v);
451-
if (x == (unsigned long long) -1 && PyErr_Occurred())
462+
int do_decref = 0; /* if nb_int was called */
463+
464+
if (!PyLong_Check(v)) {
465+
v = get_int_unless_float(v);
466+
if (NULL == v) {
452467
return -1;
468+
}
469+
do_decref = 1;
453470
}
454-
else {
455-
long long y;
456-
if (!PyArg_Parse(v, "L;array item must be integer", &y))
457-
return -1;
458-
if (y < 0) {
459-
PyErr_SetString(PyExc_OverflowError,
460-
"unsigned long long is less than minimum");
461-
return -1;
471+
x = PyLong_AsUnsignedLongLong(v);
472+
if (x == (unsigned long long)-1 && PyErr_Occurred()) {
473+
if (do_decref) {
474+
Py_DECREF(v);
462475
}
463-
x = (unsigned long long)y;
476+
return -1;
464477
}
465-
466478
if (i >= 0)
467479
((unsigned long long *)ap->ob_item)[i] = x;
480+
481+
if (do_decref) {
482+
Py_DECREF(v);
483+
}
468484
return 0;
469485
}
470486

0 commit comments

Comments
 (0)