diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 360f3ea23ec97..e631a6b4937c4 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -27,13 +27,13 @@ CategoricalDtype, ExtensionDtype, PandasExtensionDtype) from pandas.core.dtypes.generic import ( ABCDataFrame, ABCDatetimeIndex, ABCExtensionArray, ABCIndexClass, - ABCSeries) + ABCPandasArray, ABCSeries) from pandas.core.dtypes.missing import ( _isna_compat, array_equivalent, isna, notna) import pandas.core.algorithms as algos from pandas.core.arrays import ( - Categorical, DatetimeArray, ExtensionArray, TimedeltaArray) + Categorical, DatetimeArray, ExtensionArray, PandasDtype, TimedeltaArray) from pandas.core.base import PandasObject import pandas.core.common as com from pandas.core.indexes.datetimes import DatetimeIndex @@ -576,23 +576,14 @@ def _astype(self, dtype, copy=False, errors='raise', values=None, return self.make_block(Categorical(self.values, dtype=dtype)) - # convert dtypes if needed dtype = pandas_dtype(dtype) + # astype processing if is_dtype_equal(self.dtype, dtype): if copy: return self.copy() return self - klass = None - if is_sparse(self.values): - # special case sparse, Series[Sparse].astype(object) is sparse - klass = ExtensionBlock - elif is_object_dtype(dtype): - klass = ObjectBlock - elif is_extension_array_dtype(dtype): - klass = ExtensionBlock - try: # force the copy here if values is None: @@ -624,7 +615,7 @@ def _astype(self, dtype, copy=False, errors='raise', values=None, pass newb = make_block(values, placement=self.mgr_locs, - klass=klass, ndim=self.ndim) + ndim=self.ndim) except Exception: # noqa: E722 if errors == 'raise': raise @@ -3041,6 +3032,13 @@ def get_block_type(values, dtype=None): def make_block(values, placement, klass=None, ndim=None, dtype=None, fastpath=None): + # Ensure that we don't allow PandasArray / PandasDtype in internals. + # For now, blocks should be backed by ndarrays when possible. + if isinstance(values, ABCPandasArray): + values = values.to_numpy() + if isinstance(dtype, PandasDtype): + dtype = dtype.numpy_dtype + if fastpath is not None: # GH#19265 pyarrow is passing this warnings.warn("fastpath argument is deprecated, will be removed " diff --git a/pandas/tests/internals/test_internals.py b/pandas/tests/internals/test_internals.py index fc520436f02f7..f73b7842ef901 100644 --- a/pandas/tests/internals/test_internals.py +++ b/pandas/tests/internals/test_internals.py @@ -1291,3 +1291,23 @@ def test_block_shape(): assert (a._data.blocks[0].mgr_locs.indexer == b._data.blocks[0].mgr_locs.indexer) + + +def test_make_block_no_pandas_array(): + # https://github.com/pandas-dev/pandas/pull/24866 + arr = pd.array([1, 2]) + + # PandasArray, no dtype + result = make_block(arr, slice(len(arr))) + assert result.is_integer is True + assert result.is_extension is False + + # PandasArray, PandasDtype + result = make_block(arr, slice(len(arr)), dtype=arr.dtype) + assert result.is_integer is True + assert result.is_extension is False + + # ndarray, PandasDtype + result = make_block(arr.to_numpy(), slice(len(arr)), dtype=arr.dtype) + assert result.is_integer is True + assert result.is_extension is False diff --git a/pandas/tests/series/test_internals.py b/pandas/tests/series/test_internals.py index e8f5344689e6f..f6f4a2db359f7 100644 --- a/pandas/tests/series/test_internals.py +++ b/pandas/tests/series/test_internals.py @@ -312,6 +312,14 @@ def test_constructor_no_pandas_array(self): tm.assert_series_equal(ser, result) assert isinstance(result._data.blocks[0], IntBlock) + def test_astype_no_pandas_dtype(self): + # https://github.com/pandas-dev/pandas/pull/24866 + ser = pd.Series([1, 2], dtype="int64") + # Don't have PandasDtype in the public API, so we use `.array.dtype`, + # which is a PandasDtype. + result = ser.astype(ser.array.dtype) + tm.assert_series_equal(result, ser) + def test_from_array(self): result = pd.Series(pd.array(['1H', '2H'], dtype='timedelta64[ns]')) assert result._data.blocks[0].is_extension is False