@@ -1924,6 +1924,8 @@ def can_hold_element(arr: ArrayLike, element: Any) -> bool:
1924
1924
arr ._validate_setitem_value (element )
1925
1925
return True
1926
1926
except (ValueError , TypeError ):
1927
+ # TODO(2.0): stop catching ValueError for tzaware, see
1928
+ # _catch_deprecated_value_error
1927
1929
return False
1928
1930
1929
1931
# This is technically incorrect, but maintains the behavior of
@@ -1933,7 +1935,7 @@ def can_hold_element(arr: ArrayLike, element: Any) -> bool:
1933
1935
try :
1934
1936
np_can_hold_element (dtype , element )
1935
1937
return True
1936
- except (TypeError , ValueError ):
1938
+ except (TypeError , LossySetitemError ):
1937
1939
return False
1938
1940
1939
1941
@@ -1963,7 +1965,7 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
1963
1965
if isinstance (element , range ):
1964
1966
if _dtype_can_hold_range (element , dtype ):
1965
1967
return element
1966
- raise ValueError
1968
+ raise LossySetitemError
1967
1969
1968
1970
elif is_integer (element ) or (is_float (element ) and element .is_integer ()):
1969
1971
# e.g. test_setitem_series_int8 if we have a python int 1
@@ -1972,7 +1974,7 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
1972
1974
info = np .iinfo (dtype )
1973
1975
if info .min <= element <= info .max :
1974
1976
return dtype .type (element )
1975
- raise ValueError
1977
+ raise LossySetitemError
1976
1978
1977
1979
if tipo is not None :
1978
1980
if tipo .kind not in ["i" , "u" ]:
@@ -1986,10 +1988,10 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
1986
1988
# np.putmask, whereas the raw values cannot.
1987
1989
# see TestSetitemFloatNDarrayIntoIntegerSeries
1988
1990
return casted
1989
- raise ValueError
1991
+ raise LossySetitemError
1990
1992
1991
1993
# Anything other than integer we cannot hold
1992
- raise ValueError
1994
+ raise LossySetitemError
1993
1995
elif (
1994
1996
dtype .kind == "u"
1995
1997
and isinstance (element , np .ndarray )
@@ -2001,31 +2003,31 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
2001
2003
# TODO: faster to check (element >=0).all()? potential
2002
2004
# itemsize issues there?
2003
2005
return casted
2004
- raise ValueError
2006
+ raise LossySetitemError
2005
2007
elif dtype .itemsize < tipo .itemsize :
2006
- raise ValueError
2008
+ raise LossySetitemError
2007
2009
elif not isinstance (tipo , np .dtype ):
2008
2010
# i.e. nullable IntegerDtype; we can put this into an ndarray
2009
2011
# losslessly iff it has no NAs
2010
2012
if element ._hasna :
2011
- raise ValueError
2013
+ raise LossySetitemError
2012
2014
return element
2013
2015
2014
2016
return element
2015
2017
2016
- raise ValueError
2018
+ raise LossySetitemError
2017
2019
2018
2020
elif dtype .kind == "f" :
2019
2021
if tipo is not None :
2020
2022
# TODO: itemsize check?
2021
2023
if tipo .kind not in ["f" , "i" , "u" ]:
2022
2024
# Anything other than float/integer we cannot hold
2023
- raise ValueError
2025
+ raise LossySetitemError
2024
2026
elif not isinstance (tipo , np .dtype ):
2025
2027
# i.e. nullable IntegerDtype or FloatingDtype;
2026
2028
# we can put this into an ndarray losslessly iff it has no NAs
2027
2029
if element ._hasna :
2028
- raise ValueError
2030
+ raise LossySetitemError
2029
2031
return element
2030
2032
elif tipo .itemsize > dtype .itemsize :
2031
2033
if isinstance (element , np .ndarray ):
@@ -2034,13 +2036,13 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
2034
2036
# TODO(np>=1.20): we can just use np.array_equal with equal_nan
2035
2037
if array_equivalent (casted , element ):
2036
2038
return casted
2037
- raise ValueError
2039
+ raise LossySetitemError
2038
2040
2039
2041
return element
2040
2042
2041
2043
if lib .is_integer (element ) or lib .is_float (element ):
2042
2044
return element
2043
- raise ValueError
2045
+ raise LossySetitemError
2044
2046
2045
2047
elif dtype .kind == "c" :
2046
2048
if lib .is_integer (element ) or lib .is_complex (element ) or lib .is_float (element ):
@@ -2052,13 +2054,13 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
2052
2054
if casted == element :
2053
2055
return casted
2054
2056
# otherwise e.g. overflow see test_32878_complex_itemsize
2055
- raise ValueError
2057
+ raise LossySetitemError
2056
2058
2057
2059
if tipo is not None :
2058
2060
if tipo .kind in ["c" , "f" , "i" , "u" ]:
2059
2061
return element
2060
- raise ValueError
2061
- raise ValueError
2062
+ raise LossySetitemError
2063
+ raise LossySetitemError
2062
2064
2063
2065
elif dtype .kind == "b" :
2064
2066
if tipo is not None :
@@ -2067,23 +2069,23 @@ def np_can_hold_element(dtype: np.dtype, element: Any) -> Any:
2067
2069
# i.e. we have a BooleanArray
2068
2070
if element ._hasna :
2069
2071
# i.e. there are pd.NA elements
2070
- raise ValueError
2072
+ raise LossySetitemError
2071
2073
return element
2072
- raise ValueError
2074
+ raise LossySetitemError
2073
2075
if lib .is_bool (element ):
2074
2076
return element
2075
- raise ValueError
2077
+ raise LossySetitemError
2076
2078
2077
2079
elif dtype .kind == "S" :
2078
2080
# TODO: test tests.frame.methods.test_replace tests get here,
2079
2081
# need more targeted tests. xref phofl has a PR about this
2080
2082
if tipo is not None :
2081
2083
if tipo .kind == "S" and tipo .itemsize <= dtype .itemsize :
2082
2084
return element
2083
- raise ValueError
2085
+ raise LossySetitemError
2084
2086
if isinstance (element , bytes ) and len (element ) <= dtype .itemsize :
2085
2087
return element
2086
- raise ValueError
2088
+ raise LossySetitemError
2087
2089
2088
2090
raise NotImplementedError (dtype )
2089
2091
@@ -2097,3 +2099,11 @@ def _dtype_can_hold_range(rng: range, dtype: np.dtype) -> bool:
2097
2099
if not len (rng ):
2098
2100
return True
2099
2101
return np .can_cast (rng [0 ], dtype ) and np .can_cast (rng [- 1 ], dtype )
2102
+
2103
+
2104
+ class LossySetitemError (Exception ):
2105
+ """
2106
+ Raised when trying to do a __setitem__ on an np.ndarray that is not lossless.
2107
+ """
2108
+
2109
+ pass
0 commit comments