diff --git a/src/_arraykit.c b/src/_arraykit.c index f8906a5c..4980471a 100644 --- a/src/_arraykit.c +++ b/src/_arraykit.c @@ -383,7 +383,8 @@ dtype_from_element(PyObject *Py_UNUSED(m), PyObject *arg) // Integers if (PyLong_CheckExact(arg)) { - return (PyObject*)PyArray_DescrFromType(NPY_LONG); + // It turned out to be very complex to determine the dtype from manually examining the integer, as numpy is rather inscrutable + return (PyObject*)PyArray_DescrFromObject(arg, NULL); } // Bool diff --git a/test/test_util.py b/test/test_util.py index dcdc1c24..95c4f2e7 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -323,38 +323,40 @@ def test_dtype_from_element_core_dtypes(self) -> None: np.bool_, ] for dtype in dtypes: - self.assertEqual(dtype, dtype_from_element(dtype())) + self.assertEqual(dtype, dtype_from_element(dtype()), dtype) def test_dtype_from_element_str_and_misc_dtypes(self) -> None: dtype_obj_pairs = [ - (np.dtype(' None: NT = collections.namedtuple('NT', tuple('abc')) dtype_obj_pairs = [ - (np.int_, 12), - (np.float_, 12.0), - (np.bool_, True), - (np.dtype('O'), None), - (np.float_, float('NaN')), - (np.dtype('O'), object()), - (np.dtype('O'), (1, 2, 3)), - (np.dtype('O'), NT(1, 2, 3)), - (np.dtype('O'), datetime.date(2020, 12, 31)), - (np.dtype('O'), datetime.timedelta(14)), + 12, + 12.0, + True, + None, + float('NaN'), + object(), + datetime.date(2020, 12, 31), + datetime.timedelta(14), ] - for dtype, obj in dtype_obj_pairs: - self.assertEqual(dtype, dtype_from_element(obj)) + for obj in dtype_obj_pairs: + self.assertEqual(np.array(obj).dtype, dtype_from_element(obj), obj) + + # Tuples + self.assertEqual(np.object, dtype_from_element((1, 2, 3)), obj) + self.assertEqual(np.object, dtype_from_element(NT(1, 2, 3)), obj) def test_dtype_from_element_time_dtypes(self) -> None: # Datetime & Timedelta @@ -363,6 +365,24 @@ def test_dtype_from_element_time_dtypes(self) -> None: obj = ctor(12, precision) self.assertEqual(np.dtype(f'<{kind}8[{precision}]'), dtype_from_element(obj)) + def test_dtype_from_element_int_boundaries(self) -> None: + failed = False + for offset, power in itertools.product((-1, 0, 1), range(-65, 66)): + val = 2**power + offset + + actual = dtype_from_element(val) + expected = np.array(val).dtype + + if actual != expected: + print(str(val) + '. actual=' + str(actual) + ' expected=' + str(expected)) + failed = True + else: + # Check doesn't raise Overflow error + self.assertEqual(np.array(val, dtype=actual).item(), val) + self.assertEqual(np.array(val, dtype=expected).item(), val) + + self.assertTrue(not failed) + def test_dtype_from_element_str_and_bytes_dtypes(self) -> None: for size in (1, 8, 16, 32, 64, 128, 256, 512): self.assertEqual(np.dtype(f'|S{size}'), dtype_from_element(bytes(size)))