Repository: numba/numba

Issue:
Add `dtype` property to `List` class
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

For more general "how do I do X?" type questions, please speak to us in real
time on https://gitter.im/numba/numba or post to the Numba mailing list
https://groups.google.com/a/continuum.io/forum/#!forum/numba-users.

-->

## Feature request

<!--

Please include details of the feature you would like to see, why you would
like to see it/the use case

-->

add a `dtype` property to the `List` class that forwards to the `dtype` of the contained `_list_type`

Expected functionality:
```python
>>> from numba.typed import List
>>> a = List()
>>> a.append(3)
>>> a.dtype
'int64'
```


Discussion:

Patch:
diff --git a/numba/typed/listobject.py b/numba/typed/listobject.py
index 4f4071e51..0416a615d 100644
--- a/numba/typed/listobject.py
+++ b/numba/typed/listobject.py
@@ -12,6 +12,7 @@ from numba import _helperlib
 from numba.core.extending import (
     overload,
     overload_method,
+    overload_attribute,
     register_jitable,
     intrinsic,
     register_model,
@@ -1246,6 +1247,18 @@ def ol_list_sort(lst, key=None, reverse=False):
     return impl
 
 
+@overload_attribute(types.ListType, '_dtype')
+def impl_dtype(l):
+    if not isinstance(l, types.ListType):
+        return
+    dt = l.dtype
+
+    def impl(l):
+        return dt
+
+    return impl
+
+
 def _equals_helper(this, other, OP):
     if not isinstance(this, types.ListType):
         return
diff --git a/numba/typed/typedlist.py b/numba/typed/typedlist.py
index 0b0f73d6d..f2334c9f2 100644
--- a/numba/typed/typedlist.py
+++ b/numba/typed/typedlist.py
@@ -235,6 +235,12 @@ class List(MutableSequence):
         """
         return self._list_type is not None
 
+    @property
+    def _dtype(self):
+        if not self._typed:
+            raise RuntimeError("invalid operation on untyped list")
+        return self._list_type.dtype
+
     def _initialise_list(self, item):
         lsttype = types.ListType(typeof(item))
         self._list_type, self._opaque = self._parse_arg(lsttype)


Test patch:
diff --git a/numba/tests/test_typedlist.py b/numba/tests/test_typedlist.py
index 9f26b1327..ee6738dd0 100644
--- a/numba/tests/test_typedlist.py
+++ b/numba/tests/test_typedlist.py
@@ -155,6 +155,34 @@ class TestTypedList(MemoryLeakMixin, TestCase):
         self.assertEqual(L.pop(ui32_1), 2)
         self.assertEqual(L.pop(ui32_0), 123)
 
+    def test_dtype(self):
+
+        L = List.empty_list(int32)
+        self.assertEqual(L._dtype, int32)
+
+        L = List.empty_list(float32)
+        self.assertEqual(L._dtype, float32)
+
+        @njit
+        def foo():
+            li, lf = List(), List()
+            li.append(int32(1))
+            lf.append(float32(1.0))
+            return li._dtype, lf._dtype
+
+        self.assertEqual(foo(), (np.dtype('int32'), np.dtype('float32')))
+        self.assertEqual(foo.py_func(), (int32, float32))
+
+    def test_dtype_raises_exception_on_untyped_list(self):
+
+        with self.assertRaises(RuntimeError) as raises:
+            L = List()
+            L._dtype
+        self.assertIn(
+            "invalid operation on untyped list",
+            str(raises.exception),
+        )
+
     @skip_parfors_unsupported
     def test_unsigned_prange(self):
         @njit(parallel=True)