Repository: TileDB-Inc/TileDB-Py

Issue:
multi_index not accepting tuples
The [UDF apply docs](https://docs.tiledb.com/cloud/client-api/serverless-udfs#multi-index-usage) imply the tuples and slices are interchangeable.  The standard API throws an exception on tuples (for both tiledb: and s3:)


```
>>> import tiledb, tiledb.cloud
>>> A = tiledb.DenseArray("tiledb://TileDB-Inc/quickstart_dense", ctx=tiledb.cloud.Ctx())
>>> A.multi_index[[(1,2), 4], [slice(1,4)]]['a']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ec2-user/venv/lib64/python3.7/site-packages/tiledb/multirange_indexing.py", line 121, in __getitem__
    ranges = self.getitem_ranges(idx)
  File "/home/ec2-user/venv/lib64/python3.7/site-packages/tiledb/multirange_indexing.py", line 109, in getitem_ranges
    subranges = sel_to_subranges(sel)
  File "/home/ec2-user/venv/lib64/python3.7/site-packages/tiledb/multirange_indexing.py", line 60, in sel_to_subranges
    raise TypeError("Unsupported selection ")
TypeError: Unsupported selection 
```

It would be great if multi_index and UDF apply accepted the same index types.


Discussion:

Resolving patch:
diff --git a/tiledb/multirange_indexing.py b/tiledb/multirange_indexing.py
--- a/tiledb/multirange_indexing.py
+++ b/tiledb/multirange_indexing.py
@@ -8,11 +8,6 @@
 except:
     from tiledb.indexing import multi_index
 
-def _index_as_tuple(idx):
-    """Forces scalar index objects to a tuple representation"""
-    if isinstance(idx, tuple):
-        return idx
-    return (idx,)
 
 def mr_dense_result_shape(ranges, base_shape = None):
     # assumptions: len(ranges) matches number of dims
@@ -36,11 +31,6 @@ def mr_dense_result_numel(ranges):
     return np.prod(mr_dense_result_shape(ranges))
 
 def sel_to_subranges(dim_sel):
-    if isinstance(dim_sel, list):
-        dim_sel = tuple(dim_sel)
-    elif not isinstance(dim_sel, tuple):
-        dim_sel = (dim_sel,)
-
     subranges = list()
     for range in dim_sel:
         if np.isscalar(range):
@@ -53,6 +43,8 @@ def sel_to_subranges(dim_sel):
                 pass
             else:
                 subranges.append( (range.start, range.stop) )
+        elif isinstance(range, tuple):
+            subranges.extend((range,))
         elif isinstance(range, list):
             for el in range:
                 subranges.append( (el, el) )
@@ -102,10 +94,16 @@ def __test_init__(cls, array):
     def getitem_ranges(self, idx):
         dom = self.schema.domain
         ndim = dom.ndim
-        idx = _index_as_tuple(idx)
+
+        if isinstance(idx, tuple):
+            idx = list(idx)
+        else:
+            idx = [idx]
 
         ranges = list()
         for i,sel in enumerate(idx):
+            if not isinstance(sel, list):
+                sel = [sel]
             subranges = sel_to_subranges(sel)
             ranges.append(subranges)
 


Test patch:
diff --git a/tiledb/tests/test_multi_index.py b/tiledb/tests/test_multi_index.py
--- a/tiledb/tests/test_multi_index.py
+++ b/tiledb/tests/test_multi_index.py
@@ -254,6 +254,31 @@ def make_arr(ndim):
                 ((4, 6),)
             )
         )
+        self.assertEqual(
+            m.getitem_ranges( ibi[ (1,2) ]),
+            (
+                ((1,1),),
+                ((2,2),),
+                (),
+            )
+        )
+        self.assertEqual(
+            m.getitem_ranges( ibi[ [(1,2)] ]),
+            (
+                ((1,2),),
+                (),
+                (),
+            )
+        )
+        self.assertEqual(
+            m.getitem_ranges( ibi[ [ (1,2), 4], [slice(1,4)] ] ),
+            (
+                ((1,2), (4,4)),
+                ((1,4),),
+                ()
+            )
+        )
+
 
     def test_multirange_1d_dense_int64(self):
         attr_name = ''
@@ -288,6 +313,14 @@ def test_multirange_1d_dense_int64(self):
                 -10,
                 A.multi_index[-10]['coords'].view('i8')
             )
+            assert_array_equal(
+                orig_array[0:],
+                A.multi_index[ [(-10, 10)] ][attr_name]
+            )
+            assert_array_equal(
+                orig_array[0:],
+                A.multi_index[ [slice(-10, 10),] ][attr_name]
+            )
 
 
     def test_multirange_1d_sparse_double(self):