From 5d59301794f060727a17834d1b6a15e014330ea2 Mon Sep 17 00:00:00 2001 From: fengluo Date: Sat, 19 Oct 2024 20:58:13 +0800 Subject: [PATCH 1/7] allow array_finalize use default fill-value if original value can't be cast --- numpy/ma/core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 0b8273cfaa24..0e0e2a0a9875 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -3124,7 +3124,10 @@ def __array_finalize__(self, obj): # Finalize the fill_value if self._fill_value is not None: - self._fill_value = _check_fill_value(self._fill_value, self.dtype) + try: + self._fill_value = _check_fill_value(self._fill_value, self.dtype) + except TypeError: + self._fill_value = _check_fill_value(None, self.dtype) elif self.dtype.names is not None: # Finalize the default fill_value for structured arrays self._fill_value = _check_fill_value(None, self.dtype) From 9b9004c97387d3bbbe25709ffed0ba69d794bd3e Mon Sep 17 00:00:00 2001 From: fengluo Date: Sat, 19 Oct 2024 21:00:06 +0800 Subject: [PATCH 2/7] delete testing file --- my_test_.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 my_test_.py diff --git a/my_test_.py b/my_test_.py deleted file mode 100644 index e69de29bb2d1..000000000000 From 7c999568d0c9d272f0a65b53498b9d7d9c44f240 Mon Sep 17 00:00:00 2001 From: fengluo Date: Tue, 22 Oct 2024 12:23:35 +0800 Subject: [PATCH 3/7] test --- numpy/_core/src/common/npy_cpu_features.c | 1 + 1 file changed, 1 insertion(+) diff --git a/numpy/_core/src/common/npy_cpu_features.c b/numpy/_core/src/common/npy_cpu_features.c index 7c0a4c60294c..e02f2920456d 100644 --- a/numpy/_core/src/common/npy_cpu_features.c +++ b/numpy/_core/src/common/npy_cpu_features.c @@ -10,6 +10,7 @@ // this file are shared by the _simd, _umath_tests, and // _multiarray_umath modules + // Hold all CPU features boolean values static unsigned char npy__cpu_have[NPY_CPU_FEATURE_MAX]; From 28a3724a3f8ad5d65f6d80a26fa2ed43b7452005 Mon Sep 17 00:00:00 2001 From: fengluo Date: Tue, 22 Oct 2024 12:23:46 +0800 Subject: [PATCH 4/7] test --- numpy/_core/src/common/npy_cpu_features.c | 1 - 1 file changed, 1 deletion(-) diff --git a/numpy/_core/src/common/npy_cpu_features.c b/numpy/_core/src/common/npy_cpu_features.c index e02f2920456d..7c0a4c60294c 100644 --- a/numpy/_core/src/common/npy_cpu_features.c +++ b/numpy/_core/src/common/npy_cpu_features.c @@ -10,7 +10,6 @@ // this file are shared by the _simd, _umath_tests, and // _multiarray_umath modules - // Hold all CPU features boolean values static unsigned char npy__cpu_have[NPY_CPU_FEATURE_MAX]; From 50cbf23ac6464cb1abf525eb7d8086893e47ce15 Mon Sep 17 00:00:00 2001 From: fengluo Date: Sat, 2 Nov 2024 13:18:15 +0800 Subject: [PATCH 5/7] add test --- numpy/ma/tests/test_core.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 2dbad0f1bc51..ae7aa90018c8 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -2517,6 +2517,22 @@ def test_ndarray_mask(self): assert_equal(test.mask, control.mask) assert_(not isinstance(test.mask, MaskedArray)) + def test_vectorize_with_type_casting(self): + # TypeError may be raised with return dtype different to input dtype. + # See issue 27165 + f = np.vectorize(lambda c: ord(c) if c else -1, otypes=[int]) + + a = np.ma.masked_all(1, str) + x = a.fill_value # Assign default fill_value + assert_equal(f(a), np.ma.masked_all(1, np.int64)) + + a = np.ma.masked_array([""], True) + x = a.fill_value # Assign default fill_value + assert_equal(f(a), np.ma.masked_all(1, np.int64)) + a = np.ma.masked_array([""], True, fill_value="?") + assert_equal(f(a), np.ma.masked_all(1, np.int64)) + + def test_treatment_of_NotImplemented(self): # Check that NotImplemented is returned at appropriate places From 26b2359363c9b238e29a097ce349e10257068410 Mon Sep 17 00:00:00 2001 From: fengluo Date: Sat, 2 Nov 2024 14:31:39 +0800 Subject: [PATCH 6/7] add missing whitespace --- numpy/ma/tests/test_core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index ae7aa90018c8..35928a090f63 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -2523,16 +2523,15 @@ def test_vectorize_with_type_casting(self): f = np.vectorize(lambda c: ord(c) if c else -1, otypes=[int]) a = np.ma.masked_all(1, str) - x = a.fill_value # Assign default fill_value + x = a.fill_value # Assign default fill_value assert_equal(f(a), np.ma.masked_all(1, np.int64)) a = np.ma.masked_array([""], True) - x = a.fill_value # Assign default fill_value + x = a.fill_value # Assign default fill_value assert_equal(f(a), np.ma.masked_all(1, np.int64)) a = np.ma.masked_array([""], True, fill_value="?") assert_equal(f(a), np.ma.masked_all(1, np.int64)) - def test_treatment_of_NotImplemented(self): # Check that NotImplemented is returned at appropriate places From 0dabf2a785181f945f3b1c973b90a4824fa82a49 Mon Sep 17 00:00:00 2001 From: fengluo Date: Tue, 5 Nov 2024 03:47:48 +0800 Subject: [PATCH 7/7] add force casting to fill_value with fallback --- numpy/ma/core.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 0e0e2a0a9875..75af6155580e 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -498,6 +498,29 @@ def _check_fill_value(fill_value, ndtype): raise TypeError(err_msg % (fill_value, ndtype)) from e return np.array(fill_value) +def _force_fill_value_cast(fill_value, ndtype, fallback=None): + """ + Wraps `_check_fill_value` to cast the given `fill_value` into the specified dtype. + + If the `fill_value` cannot be cast into the given dtype, it returns the provided + fallback value. The result is always a 0-dimensional array if the casting is + successful. + + Parameters: + fill_value: The value to be cast. + ndtype: The target data type for casting. + fallback: The value to return if casting fails + (default is None, which will return default value). + + Returns: + A 0-dimensional array if casting is successful; otherwise, the fallback value. + """ + try: + result = _check_fill_value(fill_value, ndtype) + except Exception: + result = fallback + return result + def set_fill_value(a, fill_value): """ @@ -3124,10 +3147,8 @@ def __array_finalize__(self, obj): # Finalize the fill_value if self._fill_value is not None: - try: - self._fill_value = _check_fill_value(self._fill_value, self.dtype) - except TypeError: - self._fill_value = _check_fill_value(None, self.dtype) + self._fill_value = _force_fill_value_cast(self._fill_value, + self.dtype) elif self.dtype.names is not None: # Finalize the default fill_value for structured arrays self._fill_value = _check_fill_value(None, self.dtype) @@ -4254,11 +4275,7 @@ def _comparison(self, other, compare): # Cast fill value to np.bool if needed. If it cannot be cast, the # default boolean fill value is used. if check._fill_value is not None: - try: - fill = _check_fill_value(check._fill_value, np.bool) - except (TypeError, ValueError): - fill = _check_fill_value(None, np.bool) - check._fill_value = fill + check._fill_value = _force_fill_value_cast(check._fill_value, np.bool) return check