diff --git a/pandas/core/construction.py b/pandas/core/construction.py index 90349ee5f942f..8d3fd0c520a6d 100644 --- a/pandas/core/construction.py +++ b/pandas/core/construction.py @@ -49,7 +49,10 @@ is_object_dtype, is_timedelta64_ns_dtype, ) -from pandas.core.dtypes.dtypes import DatetimeTZDtype +from pandas.core.dtypes.dtypes import ( + DatetimeTZDtype, + PandasDtype, +) from pandas.core.dtypes.generic import ( ABCExtensionArray, ABCIndex, @@ -494,6 +497,10 @@ def sanitize_array( if isinstance(data, ma.MaskedArray): data = sanitize_masked_array(data) + if isinstance(dtype, PandasDtype): + # Avoid ending up with a PandasArray + dtype = dtype.numpy_dtype + # extract ndarray or ExtensionArray, ensure we have no PandasArray data = extract_array(data, extract_numpy=True) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index d6a8790afd998..2d4d783e57d9a 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -77,6 +77,7 @@ DatetimeTZDtype, ExtensionDtype, IntervalDtype, + PandasDtype, PeriodDtype, ) from pandas.core.dtypes.generic import ( @@ -1305,6 +1306,9 @@ def astype_array_safe( raise TypeError(msg) dtype = pandas_dtype(dtype) + if isinstance(dtype, PandasDtype): + # Ensure we don't end up with a PandasArray + dtype = dtype.numpy_dtype try: new_values = astype_array(values, dtype, copy=copy) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index da7ffbf08c34b..002473a1a5fb2 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1128,7 +1128,6 @@ def interpolate( **kwargs, ) - interp_values = maybe_coerce_values(interp_values) nbs = [self.make_block_same_class(interp_values)] return self._maybe_downcast(nbs, downcast) @@ -1903,10 +1902,7 @@ def maybe_coerce_values(values: ArrayLike) -> ArrayLike: ------- values : np.ndarray or ExtensionArray """ - - # Note: the only test that needs extract_array here is one where we - # pass PandasDtype to Series.astype, then need to extract PandasArray here. - values = extract_array(values, extract_numpy=True) + # Caller is responsible for ensuring PandasArray is already extracted. if isinstance(values, np.ndarray): values = ensure_wrapped_if_datetimelike(values) diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index 6afb071f76f10..782ffe030d711 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -71,7 +71,6 @@ ensure_block_shape, extend_blocks, get_block_type, - maybe_coerce_values, new_block, ) from pandas.core.internals.ops import ( @@ -989,7 +988,6 @@ def iget(self, i: int) -> SingleBlockManager: # shortcut for select a single-dim from a 2-dim BM bp = BlockPlacement(slice(0, len(values))) - values = maybe_coerce_values(values) nb = type(block)(values, placement=bp, ndim=1) return SingleBlockManager(nb, self.axes[1]) diff --git a/pandas/tests/extension/test_numpy.py b/pandas/tests/extension/test_numpy.py index e963cfec71b5b..7be776819e399 100644 --- a/pandas/tests/extension/test_numpy.py +++ b/pandas/tests/extension/test_numpy.py @@ -336,6 +336,19 @@ def test_merge(self, data, na_value): # Fails creating expected (key column becomes a PandasDtype because) super().test_merge(data, na_value) + @pytest.mark.parametrize( + "in_frame", + [ + True, + pytest.param( + False, + marks=pytest.mark.xfail(reason="PandasArray inconsistently extracted"), + ), + ], + ) + def test_concat(self, data, in_frame): + super().test_concat(data, in_frame) + class TestSetitem(BaseNumPyTests, base.BaseSetitemTests): @skip_nested