Skip to content

Commit 6e37b63

Browse files
authored
Merge pull request #176 from static-frame/175/no-double-tuple
2 parents bae0ba2 + f76e22e commit 6e37b63

File tree

2 files changed

+84
-27
lines changed

2 files changed

+84
-27
lines changed

src/_arraykit.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3573,7 +3573,7 @@ array_to_tuple_array(PyObject *Py_UNUSED(m), PyObject *a)
35733573
i++;
35743574
}
35753575
}
3576-
else { // ndim == 1
3576+
else if (PyArray_TYPE(input_array) != NPY_OBJECT) { // ndim == 1, not object
35773577
while (p < p_end) {
35783578
tuple = PyTuple_New(1);
35793579
if (tuple == NULL) {
@@ -3590,6 +3590,24 @@ array_to_tuple_array(PyObject *Py_UNUSED(m), PyObject *a)
35903590
i++;
35913591
}
35923592
}
3593+
else { // ndim == 1, object
3594+
while (p < p_end) {
3595+
item = *(PyObject**)PyArray_GETPTR1(input_array, i);
3596+
Py_INCREF(item); // always incref
3597+
if (PyTuple_Check(item)) {
3598+
tuple = item; // do not double pack
3599+
}
3600+
else {
3601+
tuple = PyTuple_New(1);
3602+
if (tuple == NULL) {
3603+
goto error;
3604+
}
3605+
PyTuple_SET_ITEM(tuple, 0, item); // steals reference to item
3606+
}
3607+
*p++ = tuple; // assign with new ref, no incr needed
3608+
i++;
3609+
}
3610+
}
35933611
PyArray_CLEARFLAGS((PyArrayObject *)output, NPY_ARRAY_WRITEABLE);
35943612
return output;
35953613
error:
@@ -3664,10 +3682,10 @@ ATT_iternext(ATTObject *self) {
36643682
Py_DECREF(tuple);
36653683
return NULL;
36663684
}
3667-
PyTuple_SET_ITEM(tuple, j, item); // steals reference to item
3685+
PyTuple_SET_ITEM(tuple, j, item); // steals ref
36683686
}
36693687
}
3670-
else { // ndim == 1
3688+
else if (PyArray_TYPE(array) != NPY_OBJECT) { // ndim == 1, not object
36713689
tuple = PyTuple_New(1);
36723690
if (tuple == NULL) {
36733691
return NULL;
@@ -3677,7 +3695,22 @@ ATT_iternext(ATTObject *self) {
36773695
Py_DECREF(tuple);
36783696
return NULL;
36793697
}
3680-
PyTuple_SET_ITEM(tuple, 0, item); // steals reference to item
3698+
PyTuple_SET_ITEM(tuple, 0, item); // steals ref
3699+
}
3700+
else { // ndim == 1, object
3701+
item = *(PyObject**)PyArray_GETPTR1(array, i);
3702+
Py_INCREF(item); // always incref
3703+
if (PyTuple_Check(item)) {
3704+
tuple = item; // do not double pack
3705+
}
3706+
else {
3707+
tuple = PyTuple_New(1);
3708+
if (tuple == NULL) {
3709+
Py_DECREF(item);
3710+
return NULL;
3711+
}
3712+
PyTuple_SET_ITEM(tuple, 0, item); // steals ref
3713+
}
36813714
}
36823715
self->pos++;
36833716
return tuple;

test/test_util.py

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -286,33 +286,47 @@ def test_array_deepcopy_h(self) -> None:
286286
a2 = array_deepcopy(a1, ())
287287

288288
#---------------------------------------------------------------------------
289-
def test_array2d_to_array1d_1d_a(self) -> None:
289+
def test_array_to_tuple_array_1d_a(self) -> None:
290290
a1 = np.arange(10)
291291
a2 = array_to_tuple_array(a1)
292292
self.assertEqual(a2.tolist(), [(0,), (1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,), (9,)])
293293

294-
def test_array2d_to_array1d_1d_b(self) -> None:
294+
def test_array_to_tuple_array_1d_b(self) -> None:
295295
a1 = np.array(['aaa', 'b', 'ccc'])
296296
a2 = array_to_tuple_array(a1)
297297
self.assertEqual(a2.tolist(), [('aaa',), ('b',), ('ccc',)])
298298

299-
def test_array2d_to_array1d_1d_c(self) -> None:
299+
def test_array_to_tuple_array_1d_c(self) -> None:
300300
a1 = np.array([None, 'b', 30])
301301
a2 = array_to_tuple_array(a1)
302302
self.assertEqual(a2.tolist(), [(None,), ('b',), (30,)])
303303

304-
def test_array2d_to_array1d_1d_d(self) -> None:
304+
def test_array_to_tuple_array_1d_d(self) -> None:
305305
a1 = np.array([('a', 10), ('b', 30), ('c', 5)], dtype=object)
306-
a2 = array_to_tuple_array(a1)
306+
a2 = array_to_tuple_array(a1) # from 2d
307307
self.assertEqual(a2.tolist(), [('a', 10), ('b', 30), ('c', 5)])
308+
a3 = array_to_tuple_array(a2) # from 1d
309+
self.assertEqual(a3.tolist(), [('a', 10), ('b', 30), ('c', 5)])
308310

309-
def test_array2d_to_array1d_1d_e(self) -> None:
311+
def test_array_to_tuple_array_1d_e(self) -> None:
310312
a1 = np.array([True, False, True], dtype=object)
311313
a2 = array_to_tuple_array(a1)
312314
self.assertIs(a2[0][0].__class__, bool)
313315
self.assertEqual(a2.tolist(), [(True,), (False,), (True,)])
314316

315-
def test_array2d_to_array1d_b(self) -> None:
317+
def test_array_to_tuple_array_1d_f(self) -> None:
318+
a1 = np.array([None, None, None], dtype=object)
319+
a1[0] = 3
320+
a1[1] = ('a', 30)
321+
a1[2] = (None, True, 90000000)
322+
323+
a2 = array_to_tuple_array(a1)
324+
self.assertEqual(a2.tolist(), [(3,), ('a', 30), (None, True, 90000000)])
325+
326+
a3 = array_to_tuple_array(a2)
327+
self.assertEqual(a3.tolist(), [(3,), ('a', 30), (None, True, 90000000)])
328+
329+
def test_array_to_tuple_array_b(self) -> None:
316330
a1 = np.arange(10, dtype=np.int64).reshape(5, 2)
317331
result = array_to_tuple_array(a1)
318332
assert isinstance(result[0], tuple)
@@ -322,35 +336,35 @@ def test_array2d_to_array1d_b(self) -> None:
322336
self.assertEqual(tuple(result), ((0, 1), (2, 3), (4, 5), (6, 7), (8, 9)))
323337

324338

325-
def test_array2d_to_array1d_c(self) -> None:
339+
def test_array_to_tuple_array_c(self) -> None:
326340
a1 = np.array([["a", "b"], ["ccc", "ddd"], ["ee", "ff"]])
327341
a2 = array_to_tuple_array(a1)
328342
self.assertEqual(a2.tolist(), [('a', 'b'), ('ccc', 'ddd'), ('ee', 'ff')])
329343

330-
def test_array2d_to_array1d_d(self) -> None:
344+
def test_array_to_tuple_array_d(self) -> None:
331345
a1 = np.array([[3, 5], [10, 20], [7, 2]], dtype=np.uint8)
332346
a2 = array_to_tuple_array(a1)
333347
self.assertEqual(a2.tolist(), [(3, 5), (10, 20), (7, 2)])
334348
self.assertIs(type(a2[0][0]), np.uint8)
335349

336-
def test_array2d_to_array1d_e(self) -> None:
350+
def test_array_to_tuple_array_e(self) -> None:
337351
a1 = np.arange(20, dtype=np.int64).reshape(4, 5)
338352
result = array_to_tuple_array(a1)
339353
self.assertEqual(result.tolist(), [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17, 18, 19)])
340354

341355
#---------------------------------------------------------------------------
342-
def test_array2d_tuple_iter_a(self) -> None:
356+
def test_array_to_tuple_iter_a(self) -> None:
343357
a1 = np.arange(20, dtype=np.int64).reshape(4, 5)
344358
result = list(array_to_tuple_iter(a1))
345359
self.assertEqual(len(result), 4)
346360
self.assertEqual(result, [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17, 18, 19)])
347361

348-
def test_array2d_tuple_iter_b(self) -> None:
362+
def test_array_to_tuple_iter_b(self) -> None:
349363
a1 = np.arange(20, dtype=np.int64).reshape(10, 2)
350364
result = list(array_to_tuple_iter(a1))
351365
self.assertEqual(result, [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11), (12, 13), (14, 15), (16, 17), (18, 19)])
352366

353-
def test_array2d_tuple_iter_c(self) -> None:
367+
def test_array_to_tuple_iter_c(self) -> None:
354368
a1 = np.array([['aaa', 'bb'], ['c', 'dd'], ['ee', 'fffff']])
355369
it = array_to_tuple_iter(a1)
356370
self.assertEqual(it.__length_hint__(), 3)
@@ -363,52 +377,62 @@ def test_array2d_tuple_iter_c(self) -> None:
363377
with self.assertRaises(StopIteration):
364378
next(it)
365379

366-
def test_array2d_tuple_iter_d(self) -> None:
380+
def test_array_to_tuple_iter_d(self) -> None:
367381
a1 = np.array([['aaa', 'bb'], ['c', 'dd'], ['ee', 'fffff']])
368382
it = array_to_tuple_iter(a1)
369383
# __reversed__ not implemented
370384
with self.assertRaises(TypeError):
371385
reversed(it)
372386

373-
def test_array2d_tuple_iter_e(self) -> None:
387+
def test_array_to_tuple_iter_e(self) -> None:
374388
a1 = np.array([[None, 'bb'], [None, 'dd'], [3, None]])
375389
it = array_to_tuple_iter(a1)
376390
del a1
377391
self.assertEqual(list(it), [(None, 'bb'), (None, 'dd'), (3, None)])
378392

379-
def test_array2d_tuple_iter_f(self) -> None:
393+
def test_array_to_tuple_iter_f(self) -> None:
380394
a1 = np.array([[None, 'bb'], [None, 'dd'], [3, None]])
381395
it1 = array_to_tuple_iter(a1)
382396
del a1
383397
it2 = iter(it1)
384398
self.assertEqual(list(it1), [(None, 'bb'), (None, 'dd'), (3, None)])
385399
self.assertEqual(list(it2), []) # expected behavior
386400

387-
def test_array2d_tuple_iter_g(self) -> None:
401+
def test_array_to_tuple_iter_g(self) -> None:
388402
a1 = np.array([[None, 'bb'], [None, 'dd'], [3, None]])
389403
it1 = array_to_tuple_iter(a1)
390404
it2 = array_to_tuple_iter(a1)
391405
del a1
392406
self.assertEqual(list(it1), [(None, 'bb'), (None, 'dd'), (3, None)])
393407
self.assertEqual(list(it2), [(None, 'bb'), (None, 'dd'), (3, None)])
394408

395-
def test_array2d_tuple_iter_1d_a(self) -> None:
409+
def test_array_to_tuple_iter_1d_a(self) -> None:
396410
a1 = np.array(['bb', 'c', 'aaa'])
397411
result = list(array_to_tuple_iter(a1))
398412
self.assertEqual(len(result), 3)
399413
self.assertEqual(result, [('bb',), ('c',), ('aaa',)])
400414

401-
def test_array2d_tuple_iter_1d_b(self) -> None:
415+
def test_array_to_tuple_iter_1d_b(self) -> None:
402416
a1 = np.array([20, -1, 8])
403417
result = list(array_to_tuple_iter(a1))
404418
self.assertEqual(len(result), 3)
405419
self.assertEqual(result, [(20,), (-1,), (8,)])
406420

407-
def test_array2d_tuple_iter_1d_c(self) -> None:
421+
def test_array_to_tuple_iter_1d_c(self) -> None:
408422
a1 = np.array([('a', 4), ('c', -1), ('d', 8)], dtype=object)
409-
result = list(array_to_tuple_iter(a1))
410-
self.assertEqual(len(result), 3)
411-
self.assertEqual(result, [('a', 4), ('c', -1), ('d', 8)])
423+
a2 = list(array_to_tuple_iter(a1))
424+
self.assertEqual(len(a2), 3)
425+
self.assertEqual(a2, [('a', 4), ('c', -1), ('d', 8)])
426+
427+
def test_array_to_tuple_iter_1d_d(self) -> None:
428+
a1 = np.array([None, None, None], dtype=object)
429+
a1[0] = 3
430+
a1[1] = ('a', 30)
431+
a1[2] = (None, True, 90000000)
432+
433+
a2 = list(array_to_tuple_iter(a1))
434+
self.assertEqual(a2, [(3,), ('a', 30), (None, True, 90000000)])
435+
412436

413437
#---------------------------------------------------------------------------
414438

0 commit comments

Comments
 (0)