Repository: pandas-dev/pandas
Pull request #56494 (2023-12-14 02:33:24)

TST: de-special-case test_EA_types
- [ ] closes #xxxx (Replace xxxx with the GitHub issue number)
- [ ] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [ ] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [ ] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [ ] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.



Patch:
diff --git a/.github/actions/setup-conda/action.yml b/.github/actions/setup-conda/action.yml
index 84e81b9a92..ceeebfcd1c 100644
--- a/.github/actions/setup-conda/action.yml
+++ b/.github/actions/setup-conda/action.yml
@@ -11,6 +11,6 @@ runs:
       with:
         environment-file: ${{ inputs.environment-file }}
         environment-name: test
-        condarc-file: ci/condarc.yml
+        condarc-file: ci/.condarc
         cache-environment: true
         cache-downloads: true
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 52463a462b..782340178f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -111,7 +111,7 @@ repos:
         types: [text]  # overwrite types: [rst]
         types_or: [python, rst]
 -   repo: https://github.com/sphinx-contrib/sphinx-lint
-    rev: v0.9.0
+    rev: v0.9.1
     hooks:
     - id: sphinx-lint
 -   repo: https://github.com/pre-commit/mirrors-clang-format
diff --git a/ci/condarc.yml b/ci/.condarc
similarity index 100%
rename from ci/condarc.yml
rename to ci/.condarc
diff --git a/pandas/core/resample.py b/pandas/core/resample.py
index b3e86b4360..39b2f43367 100644
--- a/pandas/core/resample.py
+++ b/pandas/core/resample.py
@@ -2784,7 +2784,13 @@ def asfreq(
             how = "E"
 
         if isinstance(freq, BaseOffset):
-            freq = freq_to_period_freqstr(freq.n, freq.name)
+            if hasattr(freq, "_period_dtype_code"):
+                freq = freq_to_period_freqstr(freq.n, freq.name)
+            else:
+                raise ValueError(
+                    f"Invalid offset: '{freq.base}' for converting time series "
+                    f"with PeriodIndex."
+                )
 
         new_obj = obj.copy()
         new_obj.index = obj.index.asfreq(freq, how=how)


Test patch:
diff --git a/pandas/tests/extension/base/io.py b/pandas/tests/extension/base/io.py
index c369ec8a16..3a6f2eb5ba 100644
--- a/pandas/tests/extension/base/io.py
+++ b/pandas/tests/extension/base/io.py
@@ -5,11 +5,31 @@ import pytest
 
 import pandas as pd
 import pandas._testing as tm
+from pandas.core.arrays import ExtensionArray
 
 
 class BaseParsingTests:
     @pytest.mark.parametrize("engine", ["c", "python"])
-    def test_EA_types(self, engine, data):
+    def test_EA_types(self, engine, data, request):
+        if isinstance(data.dtype, pd.CategoricalDtype):
+            # in parsers.pyx _convert_with_dtype there is special-casing for
+            #  Categorical that pre-empts _from_sequence_of_strings
+            pass
+        elif isinstance(data.dtype, pd.core.dtypes.dtypes.NumpyEADtype):
+            # These get unwrapped internally so are treated as numpy dtypes
+            #  in the parsers.pyx code
+            pass
+        elif (
+            type(data)._from_sequence_of_strings.__func__
+            is ExtensionArray._from_sequence_of_strings.__func__
+        ):
+            # i.e. the EA hasn't overridden _from_sequence_of_strings
+            mark = pytest.mark.xfail(
+                reason="_from_sequence_of_strings not implemented",
+                raises=NotImplementedError,
+            )
+            request.node.add_marker(mark)
+
         df = pd.DataFrame({"with_dtype": pd.Series(data, dtype=str(data.dtype))})
         csv_output = df.to_csv(index=False, na_rep=np.nan)
         result = pd.read_csv(
diff --git a/pandas/tests/extension/test_datetime.py b/pandas/tests/extension/test_datetime.py
index 5a7b15ddb0..efb88dc7bd 100644
--- a/pandas/tests/extension/test_datetime.py
+++ b/pandas/tests/extension/test_datetime.py
@@ -113,12 +113,6 @@ class TestDatetimeArray(base.ExtensionTests):
         result = data.map(lambda x: x, na_action=na_action)
         tm.assert_extension_array_equal(result, data)
 
-    @pytest.mark.parametrize("engine", ["c", "python"])
-    def test_EA_types(self, engine, data):
-        expected_msg = r".*must implement _from_sequence_of_strings.*"
-        with pytest.raises(NotImplementedError, match=expected_msg):
-            super().test_EA_types(engine, data)
-
     def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
         if op_name in ["median", "mean", "std"]:
             alt = ser.astype("int64")
diff --git a/pandas/tests/extension/test_interval.py b/pandas/tests/extension/test_interval.py
index b427c12d2d..98dd1c5cb6 100644
--- a/pandas/tests/extension/test_interval.py
+++ b/pandas/tests/extension/test_interval.py
@@ -90,12 +90,6 @@ class TestIntervalArray(base.ExtensionTests):
     def test_fillna_length_mismatch(self, data_missing):
         super().test_fillna_length_mismatch(data_missing)
 
-    @pytest.mark.parametrize("engine", ["c", "python"])
-    def test_EA_types(self, engine, data):
-        expected_msg = r".*must implement _from_sequence_of_strings.*"
-        with pytest.raises(NotImplementedError, match=expected_msg):
-            super().test_EA_types(engine, data)
-
 
 # TODO: either belongs in tests.arrays.interval or move into base tests.
 def test_fillna_non_scalar_raises(data_missing):
diff --git a/pandas/tests/extension/test_sparse.py b/pandas/tests/extension/test_sparse.py
index 8e46b90d4d..5ed8d13e1a 100644
--- a/pandas/tests/extension/test_sparse.py
+++ b/pandas/tests/extension/test_sparse.py
@@ -446,11 +446,7 @@ class TestPrinting(BaseSparseTests, base.BasePrintingTests):
 
 
 class TestParsing(BaseSparseTests, base.BaseParsingTests):
-    @pytest.mark.parametrize("engine", ["c", "python"])
-    def test_EA_types(self, engine, data):
-        expected_msg = r".*must implement _from_sequence_of_strings.*"
-        with pytest.raises(NotImplementedError, match=expected_msg):
-            super().test_EA_types(engine, data)
+    pass
 
 
 class TestNoNumericAccumulations(base.BaseAccumulateTests):
diff --git a/pandas/tests/resample/test_period_index.py b/pandas/tests/resample/test_period_index.py
index 60b222179b..703abbca91 100644
--- a/pandas/tests/resample/test_period_index.py
+++ b/pandas/tests/resample/test_period_index.py
@@ -970,6 +970,22 @@ class TestPeriodIndex:
             result = ser.resample("T").mean()
         tm.assert_series_equal(result, expected)
 
+    @pytest.mark.parametrize(
+        "offset",
+        [
+            offsets.MonthBegin(),
+            offsets.BYearBegin(2),
+            offsets.BusinessHour(2),
+        ],
+    )
+    def test_asfreq_invalid_period_freq(self, offset, series_and_frame):
+        # GH#9586
+        msg = f"Invalid offset: '{offset.base}' for converting time series "
+
+        df = series_and_frame
+        with pytest.raises(ValueError, match=msg):
+            df.asfreq(freq=offset)
+
 
 @pytest.mark.parametrize(
     "freq,freq_depr",