diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 1cf452b4a6c2c..7721c90c9b4b4 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -219,7 +219,7 @@ class CategoricalDtype(PandasExtensionDtype, ExtensionDtype): kind = "O" # type: str_type str = "|O08" base = np.dtype("O") - _metadata = ("categories", "ordered") + _metadata = ("categories", "ordered", "_ordered_from_sentinel") _cache = {} # type: Dict[str_type, PandasExtensionDtype] def __init__(self, categories=None, ordered: OrderedType = ordered_sentinel): @@ -356,6 +356,7 @@ def __setstate__(self, state: Dict[str_type, Any]) -> None: # pickle -> need to set the settable private ones here (see GH26067) self._categories = state.pop("categories", None) self._ordered = state.pop("ordered", False) + self._ordered_from_sentinel = state.pop("_ordered_from_sentinel", False) def __hash__(self) -> int: # _hash_categories returns a uint64, so use the negative diff --git a/pandas/tests/dtypes/test_dtypes.py b/pandas/tests/dtypes/test_dtypes.py index a81c57537408c..d3f0d7c43ee6b 100644 --- a/pandas/tests/dtypes/test_dtypes.py +++ b/pandas/tests/dtypes/test_dtypes.py @@ -903,6 +903,19 @@ def test_ordered_none_default_deprecated(self, ordered): with tm.assert_produces_warning(warning): dtype.ordered + @pytest.mark.parametrize("ordered", [True, False, None, ordered_sentinel]) + def test_pickle_ordered_from_sentinel(self, ordered): + # GH 27295: can remove test when _ordered_from_sentinel is removed (GH 26403) + dtype = CategoricalDtype(categories=list("abc"), ordered=ordered) + + warning = FutureWarning if ordered is ordered_sentinel else None + with tm.assert_produces_warning(warning, check_stacklevel=False): + dtype_from_pickle = tm.round_trip_pickle(dtype) + + result = dtype_from_pickle._ordered_from_sentinel + expected = ordered is ordered_sentinel + assert result is expected + @pytest.mark.parametrize( "dtype", [CategoricalDtype, IntervalDtype, DatetimeTZDtype, PeriodDtype] diff --git a/pandas/tests/series/test_io.py b/pandas/tests/series/test_io.py index 0238314122462..5389390501b32 100644 --- a/pandas/tests/series/test_io.py +++ b/pandas/tests/series/test_io.py @@ -226,6 +226,15 @@ def test_pickle_preserve_name(self): unpickled = self._pickle_roundtrip_name(tm.makeTimeSeries(name=n)) assert unpickled.name == n + def test_pickle_categorical_ordered_from_sentinel(self): + # GH 27295: can remove test when _ordered_from_sentinel is removed (GH 26403) + s = Series(["a", "b", "c", "a"], dtype="category") + result = tm.round_trip_pickle(s) + result = result.astype("category") + + tm.assert_series_equal(result, s) + assert result.dtype._ordered_from_sentinel is False + def _pickle_roundtrip_name(self, obj): with ensure_clean() as path: