Skip to content

Commit d44be7d

Browse files
committed
implement handling for tuples in object dtypes
1 parent 85a9e08 commit d44be7d

File tree

2 files changed

+51
-21
lines changed

2 files changed

+51
-21
lines changed

src/_arraykit.c

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3592,20 +3592,22 @@ array_to_tuple_array(PyObject *Py_UNUSED(m), PyObject *a)
35923592
}
35933593
else { // ndim == 1, object
35943594
while (p < p_end) {
3595-
tuple = PyTuple_New(1);
3596-
if (tuple == NULL) {
3597-
goto error;
3598-
}
3599-
// scalar returned in is native PyObject from object arrays
36003595
item = *(PyObject**)PyArray_GETPTR1(input_array, i);
3601-
Py_INCREF(item);
3602-
// TODO: identify tuple
3603-
PyTuple_SET_ITEM(tuple, 0, item); // steals reference to item
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+
}
36043607
*p++ = tuple; // assign with new ref, no incr needed
36053608
i++;
36063609
}
36073610
}
3608-
36093611
PyArray_CLEARFLAGS((PyArrayObject *)output, NPY_ARRAY_WRITEABLE);
36103612
return output;
36113613
error:
@@ -3680,10 +3682,10 @@ ATT_iternext(ATTObject *self) {
36803682
Py_DECREF(tuple);
36813683
return NULL;
36823684
}
3683-
PyTuple_SET_ITEM(tuple, j, item); // steals reference to item
3685+
PyTuple_SET_ITEM(tuple, j, item); // steals ref
36843686
}
36853687
}
3686-
else { // ndim == 1
3688+
else if (PyArray_TYPE(array) != NPY_OBJECT) { // ndim == 1, not object
36873689
tuple = PyTuple_New(1);
36883690
if (tuple == NULL) {
36893691
return NULL;
@@ -3693,7 +3695,22 @@ ATT_iternext(ATTObject *self) {
36933695
Py_DECREF(tuple);
36943696
return NULL;
36953697
}
3696-
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+
}
36973714
}
36983715
self->pos++;
36993716
return tuple;

test/test_util.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -286,34 +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)
306306
a2 = array_to_tuple_array(a1) # from 2d
307+
self.assertEqual(a2.tolist(), [('a', 10), ('b', 30), ('c', 5)])
307308
a3 = array_to_tuple_array(a2) # from 1d
308309
self.assertEqual(a3.tolist(), [('a', 10), ('b', 30), ('c', 5)])
309310

310-
def test_array2d_to_array1d_1d_e(self) -> None:
311+
def test_array_to_tuple_array_1d_e(self) -> None:
311312
a1 = np.array([True, False, True], dtype=object)
312313
a2 = array_to_tuple_array(a1)
313314
self.assertIs(a2[0][0].__class__, bool)
314315
self.assertEqual(a2.tolist(), [(True,), (False,), (True,)])
315316

316-
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:
317330
a1 = np.arange(10, dtype=np.int64).reshape(5, 2)
318331
result = array_to_tuple_array(a1)
319332
assert isinstance(result[0], tuple)
@@ -323,18 +336,18 @@ def test_array2d_to_array1d_b(self) -> None:
323336
self.assertEqual(tuple(result), ((0, 1), (2, 3), (4, 5), (6, 7), (8, 9)))
324337

325338

326-
def test_array2d_to_array1d_c(self) -> None:
339+
def test_array_to_tuple_array_c(self) -> None:
327340
a1 = np.array([["a", "b"], ["ccc", "ddd"], ["ee", "ff"]])
328341
a2 = array_to_tuple_array(a1)
329342
self.assertEqual(a2.tolist(), [('a', 'b'), ('ccc', 'ddd'), ('ee', 'ff')])
330343

331-
def test_array2d_to_array1d_d(self) -> None:
344+
def test_array_to_tuple_array_d(self) -> None:
332345
a1 = np.array([[3, 5], [10, 20], [7, 2]], dtype=np.uint8)
333346
a2 = array_to_tuple_array(a1)
334347
self.assertEqual(a2.tolist(), [(3, 5), (10, 20), (7, 2)])
335348
self.assertIs(type(a2[0][0]), np.uint8)
336349

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

0 commit comments

Comments
 (0)