From 1e6d28387a16f55f0e791ba9cad1079ea45f6fab Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 18 Jan 2022 10:33:57 -0800 Subject: [PATCH] REF: move np_can_hold_element to mgr method --- pandas/core/frame.py | 18 +++++------------- pandas/core/internals/base.py | 17 +++++++++++++++-- pandas/core/series.py | 7 ------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index d08c59338af9e..88b848f34fbe5 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -93,7 +93,6 @@ ) from pandas.core.dtypes.cast import ( - can_hold_element, construct_1d_arraylike_from_scalar, construct_2d_arraylike_from_scalar, find_common_type, @@ -3882,18 +3881,11 @@ def _set_value( series = self._get_item_cache(col) loc = self.index.get_loc(index) - dtype = series.dtype - if isinstance(dtype, np.dtype) and dtype.kind not in ["m", "M"]: - # otherwise we have EA values, and this check will be done - # via setitem_inplace - if not can_hold_element(series._values, value): - # We'll go through loc and end up casting. - raise TypeError - - series._mgr.setitem_inplace(loc, value) - # Note: trying to use series._set_value breaks tests in - # tests.frame.indexing.test_indexing and tests.indexing.test_partial - except (KeyError, TypeError): + + # series._set_value will do validation that may raise TypeError + # or ValueError + series._set_value(loc, value, takeable=True) + except (KeyError, TypeError, ValueError): # set using a non-recursive method & reset the cache if takeable: self.iloc[index, col] = value diff --git a/pandas/core/internals/base.py b/pandas/core/internals/base.py index 150214b149f3e..d8d1b6a34526c 100644 --- a/pandas/core/internals/base.py +++ b/pandas/core/internals/base.py @@ -9,6 +9,8 @@ final, ) +import numpy as np + from pandas._typing import ( ArrayLike, DtypeObj, @@ -16,7 +18,10 @@ ) from pandas.errors import AbstractMethodError -from pandas.core.dtypes.cast import find_common_type +from pandas.core.dtypes.cast import ( + find_common_type, + np_can_hold_element, +) from pandas.core.base import PandasObject from pandas.core.indexes.api import ( @@ -171,7 +176,15 @@ def setitem_inplace(self, indexer, value) -> None: in place, not returning a new Manager (and Block), and thus never changing the dtype. """ - self.array[indexer] = value + arr = self.array + + # EAs will do this validation in their own __setitem__ methods. + if isinstance(arr, np.ndarray): + # Note: checking for ndarray instead of np.dtype means we exclude + # dt64/td64, which do their own validation. + value = np_can_hold_element(arr.dtype, value) + + arr[indexer] = value def grouped_reduce(self, func, ignore_failures: bool = False): """ diff --git a/pandas/core/series.py b/pandas/core/series.py index 14a2881d7b291..4e69596539bed 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -62,7 +62,6 @@ ) from pandas.core.dtypes.cast import ( - can_hold_element, convert_dtypes, maybe_box_native, maybe_cast_pointwise_result, @@ -1151,12 +1150,6 @@ def __setitem__(self, key, value) -> None: def _set_with_engine(self, key, value) -> None: loc = self.index.get_loc(key) - dtype = self.dtype - if isinstance(dtype, np.dtype) and dtype.kind not in ["m", "M"]: - # otherwise we have EA values, and this check will be done - # via setitem_inplace - if not can_hold_element(self._values, value): - raise ValueError # this is equivalent to self._values[key] = value self._mgr.setitem_inplace(loc, value)