Repository: pandas-dev/pandas (python)

Issue:
ENH: raise TypeError if offsets which are not supported as period frequency pass to asfreq  
### Pandas version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the [latest version](https://pandas.pydata.org/docs/whatsnew/index.html) of pandas.

- [X] I have confirmed this bug exists on the [main branch](https://pandas.pydata.org/docs/dev/getting_started/install.html#installing-the-development-version-of-pandas) of pandas.


### Reproducible Example

```python
import pandas as pd

index = pd.PeriodIndex(["2020-01-01", "2021-01-01"], freq="M")
df = pd.DataFrame({"a": pd.Series([0, 1], index=index)})
print(df.index)
expected = df.asfreq(freq="2MS")
```


### Issue Description

In this case we get 
`AttributeError: 'pandas._libs.tslibs.offsets.MonthBegin' object has no attribute '_period_dtype_code'`

### Expected Behavior

the expected message should be
`TypeError: MS is not supported as period frequency`

### Installed Versions

<details>

Replace this line with the output of pd.show_versions()

</details>


Pull request description:
TST: assert that informative error is raised when offset not supported as a period frequency is passed to DataFrame.asfreq
- [x] closes #56718 (Replace xxxx with the GitHub issue number)
- [x] [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 (python -m pytest pandas/tests/frame/methods/test_asfreq.py)
- [x] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [x] N/A: Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [x] N/A: 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/ci/code_checks.sh b/ci/code_checks.sh
index 9c9cd2cc66..75fb82b1e2 100755
--- a/ci/code_checks.sh
+++ b/ci/code_checks.sh
@@ -137,13 +137,74 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.Interval\
         pandas.Grouper\
         pandas.core.groupby.DataFrameGroupBy.nth\
-        pandas.core.groupby.DataFrameGroupBy.rolling\
         pandas.core.groupby.SeriesGroupBy.nth\
-        pandas.core.groupby.SeriesGroupBy.rolling\
         pandas.core.groupby.DataFrameGroupBy.plot\
         pandas.core.groupby.SeriesGroupBy.plot # There should be no backslash in the final line, please keep this comment in the last ignored function
     RET=$(($RET + $?)) ; echo $MSG "DONE"
 
+    MSG='Partially validate docstrings (SA05)' ;  echo $MSG
+    $BASE_DIR/scripts/validate_docstrings.py --format=actions --errors=SA05 --ignore_functions \
+        pandas.DataFrame.agg\
+        pandas.DataFrame.aggregate\
+        pandas.DataFrame.boxplot\
+        pandas.PeriodIndex.asfreq\
+        pandas.arrays.ArrowStringArray\
+        pandas.arrays.StringArray\
+        pandas.core.groupby.DataFrameGroupBy.first\
+        pandas.core.groupby.DataFrameGroupBy.last\
+        pandas.core.groupby.SeriesGroupBy.first\
+        pandas.core.groupby.SeriesGroupBy.last\
+        pandas.core.resample.Resampler.first\
+        pandas.core.resample.Resampler.last\
+        pandas.core.window.ewm.ExponentialMovingWindow.corr\
+        pandas.core.window.ewm.ExponentialMovingWindow.cov\
+        pandas.core.window.ewm.ExponentialMovingWindow.mean\
+        pandas.core.window.ewm.ExponentialMovingWindow.std\
+        pandas.core.window.ewm.ExponentialMovingWindow.sum\
+        pandas.core.window.ewm.ExponentialMovingWindow.var\
+        pandas.core.window.expanding.Expanding.aggregate\
+        pandas.core.window.expanding.Expanding.apply\
+        pandas.core.window.expanding.Expanding.corr\
+        pandas.core.window.expanding.Expanding.count\
+        pandas.core.window.expanding.Expanding.cov\
+        pandas.core.window.expanding.Expanding.kurt\
+        pandas.core.window.expanding.Expanding.max\
+        pandas.core.window.expanding.Expanding.mean\
+        pandas.core.window.expanding.Expanding.median\
+        pandas.core.window.expanding.Expanding.min\
+        pandas.core.window.expanding.Expanding.quantile\
+        pandas.core.window.expanding.Expanding.rank\
+        pandas.core.window.expanding.Expanding.sem\
+        pandas.core.window.expanding.Expanding.skew\
+        pandas.core.window.expanding.Expanding.std\
+        pandas.core.window.expanding.Expanding.sum\
+        pandas.core.window.expanding.Expanding.var\
+        pandas.core.window.rolling.Rolling.aggregate\
+        pandas.core.window.rolling.Rolling.apply\
+        pandas.core.window.rolling.Rolling.corr\
+        pandas.core.window.rolling.Rolling.count\
+        pandas.core.window.rolling.Rolling.cov\
+        pandas.core.window.rolling.Rolling.kurt\
+        pandas.core.window.rolling.Rolling.max\
+        pandas.core.window.rolling.Rolling.mean\
+        pandas.core.window.rolling.Rolling.median\
+        pandas.core.window.rolling.Rolling.min\
+        pandas.core.window.rolling.Rolling.quantile\
+        pandas.core.window.rolling.Rolling.rank\
+        pandas.core.window.rolling.Rolling.sem\
+        pandas.core.window.rolling.Rolling.skew\
+        pandas.core.window.rolling.Rolling.std\
+        pandas.core.window.rolling.Rolling.sum\
+        pandas.core.window.rolling.Rolling.var\
+        pandas.core.window.rolling.Window.mean\
+        pandas.core.window.rolling.Window.std\
+        pandas.core.window.rolling.Window.sum\
+        pandas.core.window.rolling.Window.var\
+        pandas.plotting.bootstrap_plot\
+        pandas.plotting.boxplot\
+        pandas.plotting.radviz # There should be no backslash in the final line, please keep this comment in the last ignored function
+    RET=$(($RET + $?)) ; echo $MSG "DONE"
+
 fi
 
 ### DOCUMENTATION NOTEBOOKS ###
diff --git a/doc/source/whatsnew/v2.2.1.rst b/doc/source/whatsnew/v2.2.1.rst
index eb6e50c1be..94e26ff6aa 100644
--- a/doc/source/whatsnew/v2.2.1.rst
+++ b/doc/source/whatsnew/v2.2.1.rst
@@ -17,6 +17,7 @@ Fixed regressions
 - Fixed performance regression in :meth:`Series.combine_first` (:issue:`55845`)
 - Fixed regression in :func:`concat` changing long-standing behavior that always sorted the non-concatenation axis when the axis was a :class:`DatetimeIndex` (:issue:`57006`)
 - Fixed regression in :func:`merge_ordered` raising ``TypeError`` for ``fill_method="ffill"`` and ``how="left"`` (:issue:`57010`)
+- Fixed regression in :func:`pandas.testing.assert_series_equal` defaulting to ``check_exact=True`` when checking the :class:`Index` (:issue:`57067`)
 - Fixed regression in :func:`wide_to_long` raising an ``AttributeError`` for string columns (:issue:`57066`)
 - Fixed regression in :meth:`.DataFrameGroupBy.idxmin`, :meth:`.DataFrameGroupBy.idxmax`, :meth:`.SeriesGroupBy.idxmin`, :meth:`.SeriesGroupBy.idxmax` ignoring the ``skipna`` argument (:issue:`57040`)
 - Fixed regression in :meth:`.DataFrameGroupBy.idxmin`, :meth:`.DataFrameGroupBy.idxmax`, :meth:`.SeriesGroupBy.idxmin`, :meth:`.SeriesGroupBy.idxmax` where values containing the minimum or maximum value for the dtype could produce incorrect results (:issue:`57040`)
@@ -24,6 +25,7 @@ Fixed regressions
 - Fixed regression in :meth:`DataFrame.groupby` raising ``ValueError`` when grouping by a :class:`Series` in some cases (:issue:`57276`)
 - Fixed regression in :meth:`DataFrame.loc` raising ``IndexError`` for non-unique, masked dtype indexes where result has more than 10,000 rows (:issue:`57027`)
 - Fixed regression in :meth:`DataFrame.merge` raising ``ValueError`` for certain types of 3rd-party extension arrays (:issue:`57316`)
+- Fixed regression in :meth:`DataFrame.shift` raising ``AssertionError`` for ``axis=1`` and empty :class:`DataFrame` (:issue:`57301`)
 - Fixed regression in :meth:`DataFrame.sort_index` not producing a stable sort for a index with duplicates (:issue:`57151`)
 - Fixed regression in :meth:`DataFrame.to_dict` with ``orient='list'`` and datetime or timedelta types returning integers (:issue:`54824`)
 - Fixed regression in :meth:`DataFrame.to_json` converting nullable integers to floats (:issue:`57224`)
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index 6699e587b4..915b774430 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -120,6 +120,7 @@ Removal of prior version deprecations/changes
 - Removed ``StataReader.close`` (:issue:`49228`)
 - Removed ``axis`` argument from :meth:`DataFrame.groupby`, :meth:`Series.groupby`, :meth:`DataFrame.rolling`, :meth:`Series.rolling`, :meth:`DataFrame.resample`, and :meth:`Series.resample` (:issue:`51203`)
 - Removed ``axis`` argument from all groupby operations (:issue:`50405`)
+- Removed ``convert_dtype`` from :meth:`Series.apply` (:issue:`52257`)
 - Removed ``pandas.api.types.is_interval`` and ``pandas.api.types.is_period``, use ``isinstance(obj, pd.Interval)`` and ``isinstance(obj, pd.Period)`` instead (:issue:`55264`)
 - Removed ``pandas.io.sql.execute`` (:issue:`50185`)
 - Removed ``pandas.value_counts``, use :meth:`Series.value_counts` instead (:issue:`53493`)
@@ -134,6 +135,7 @@ Removal of prior version deprecations/changes
 - Removed unused arguments ``*args`` and ``**kwargs`` in :class:`Resampler` methods (:issue:`50977`)
 - Unrecognized timezones when parsing strings to datetimes now raises a ``ValueError`` (:issue:`51477`)
 
+
 .. ---------------------------------------------------------------------------
 .. _whatsnew_300.performance:
 
diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c
index 8bba95dd45..74ca8ead3d 100644
--- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c
+++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c
@@ -447,8 +447,15 @@ static void NpyArrPassThru_iterEnd(JSOBJ obj, JSONTypeContext *tc) {
   npyarr->curdim--;
   npyarr->dataptr -= npyarr->stride * npyarr->index[npyarr->stridedim];
   npyarr->stridedim -= npyarr->inc;
-  npyarr->dim = PyArray_DIM(npyarr->array, npyarr->stridedim);
-  npyarr->stride = PyArray_STRIDE(npyarr->array, npyarr->stridedim);
+
+  if (!PyArray_Check(npyarr->array)) {
+    PyErr_SetString(PyExc_TypeError,
+                    "NpyArrayPassThru_iterEnd received a non-array object");
+    return;
+  }
+  const PyArrayObject *arrayobj = (const PyArrayObject *)npyarr->array;
+  npyarr->dim = PyArray_DIM(arrayobj, npyarr->stridedim);
+  npyarr->stride = PyArray_STRIDE(arrayobj, npyarr->stridedim);
   npyarr->dataptr += npyarr->stride;
 
   NpyArr_freeItemValue(obj, tc);
@@ -467,12 +474,19 @@ static int NpyArr_iterNextItem(JSOBJ obj, JSONTypeContext *tc) {
 
   NpyArr_freeItemValue(obj, tc);
 
-  if (PyArray_ISDATETIME(npyarr->array)) {
+  if (!PyArray_Check(npyarr->array)) {
+    PyErr_SetString(PyExc_TypeError,
+                    "NpyArr_iterNextItem received a non-array object");
+    return 0;
+  }
+  PyArrayObject *arrayobj = (PyArrayObject *)npyarr->array;
+
+  if (PyArray_ISDATETIME(arrayobj)) {
     GET_TC(tc)->itemValue = obj;
     Py_INCREF(obj);
-    ((PyObjectEncoder *)tc->encoder)->npyType = PyArray_TYPE(npyarr->array);
+    ((PyObjectEncoder *)tc->encoder)->npyType = PyArray_TYPE(arrayobj);
     // Also write the resolution (unit) of the ndarray
-    PyArray_Descr *dtype = PyArray_DESCR(npyarr->array);
+    PyArray_Descr *dtype = PyArray_DESCR(arrayobj);
     ((PyObjectEncoder *)tc->encoder)->valueUnit =
         get_datetime_metadata_from_dtype(dtype).base;
     ((PyObjectEncoder *)tc->encoder)->npyValue = npyarr->dataptr;
@@ -505,8 +519,15 @@ static int NpyArr_iterNext(JSOBJ _obj, JSONTypeContext *tc) {
 
   npyarr->curdim++;
   npyarr->stridedim += npyarr->inc;
-  npyarr->dim = PyArray_DIM(npyarr->array, npyarr->stridedim);
-  npyarr->stride = PyArray_STRIDE(npyarr->array, npyarr->stridedim);
+  if (!PyArray_Check(npyarr->array)) {
+    PyErr_SetString(PyExc_TypeError,
+                    "NpyArr_iterNext received a non-array object");
+    return 0;
+  }
+  const PyArrayObject *arrayobj = (const PyArrayObject *)npyarr->array;
+
+  npyarr->dim = PyArray_DIM(arrayobj, npyarr->stridedim);
+  npyarr->stride = PyArray_STRIDE(arrayobj, npyarr->stridedim);
   npyarr->index[npyarr->stridedim] = 0;
 
   ((PyObjectEncoder *)tc->encoder)->npyCtxtPassthru = npyarr;
@@ -1610,7 +1631,14 @@ ISITERABLE:
       if (!values) {
         goto INVALID;
       }
-      pc->columnLabelsLen = PyArray_DIM(pc->newObj, 0);
+
+      if (!PyArray_Check(pc->newObj)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "Object_beginTypeContext received a non-array object");
+        goto INVALID;
+      }
+      const PyArrayObject *arrayobj = (const PyArrayObject *)pc->newObj;
+      pc->columnLabelsLen = PyArray_DIM(arrayobj, 0);
       pc->columnLabels = NpyArr_encodeLabels((PyArrayObject *)values, enc,
                                              pc->columnLabelsLen);
       if (!pc->columnLabels) {
diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py
index 500f805499..3672cdb13d 100644
--- a/pandas/core/algorithms.py
+++ b/pandas/core/algorithms.py
@@ -1635,7 +1635,6 @@ def map_array(
     arr: ArrayLike,
     mapper,
     na_action: Literal["ignore"] | None = None,
-    convert: bool = True,
 ) -> np.ndarray | ExtensionArray | Index:
     """
     Map values using an input mapping or function.
@@ -1647,9 +1646,6 @@ def map_array(
     na_action : {None, 'ignore'}, default None
         If 'ignore', propagate NA values, without passing them to the
         mapping correspondence.
-    convert : bool, default True
-        Try to find better dtype for elementwise function results. If
-        False, leave as dtype=object.
 
     Returns
     -------
@@ -1707,8 +1703,6 @@ def map_array(
     # we must convert to python types
     values = arr.astype(object, copy=False)
     if na_action is None:
-        return lib.map_infer(values, mapper, convert=convert)
+        return lib.map_infer(values, mapper)
     else:
-        return lib.map_infer_mask(
-            values, mapper, mask=isna(values).view(np.uint8), convert=convert
-        )
+        return lib.map_infer_mask(values, mapper, mask=isna(values).view(np.uint8))
diff --git a/pandas/core/apply.py b/pandas/core/apply.py
index 8bc1392f3c..0dabb76c25 100644
--- a/pandas/core/apply.py
+++ b/pandas/core/apply.py
@@ -16,7 +16,6 @@ import warnings
 
 import numpy as np
 
-from pandas._libs import lib
 from pandas._libs.internals import BlockValuesRefs
 from pandas._typing import (
     AggFuncType,
@@ -1376,23 +1375,10 @@ class SeriesApply(NDFrameApply):
         obj: Series,
         func: AggFuncType,
         *,
-        convert_dtype: bool | lib.NoDefault = lib.no_default,
         by_row: Literal[False, "compat", "_compat"] = "compat",
         args,
         kwargs,
     ) -> None:
-        if convert_dtype is lib.no_default:
-            convert_dtype = True
-        else:
-            warnings.warn(
-                "the convert_dtype parameter is deprecated and will be removed in a "
-                "future version.  Do ``ser.astype(object).apply()`` "
-                "instead if you want ``convert_dtype=False``.",
-                FutureWarning,
-                stacklevel=find_stack_level(),
-            )
-        self.convert_dtype = convert_dtype
-
         super().__init__(
             obj,
             func,
@@ -1486,9 +1472,7 @@ class SeriesApply(NDFrameApply):
         # TODO: remove the `na_action="ignore"` when that default has been changed in
         #  Categorical (GH51645).
         action = "ignore" if isinstance(obj.dtype, CategoricalDtype) else None
-        mapped = obj._map_values(
-            mapper=curried, na_action=action, convert=self.convert_dtype
-        )
+        mapped = obj._map_values(mapper=curried, na_action=action)
 
         if len(mapped) and isinstance(mapped[0], ABCSeries):
             # GH#43986 Need to do list(mapped) in order to get treated as nested
diff --git a/pandas/core/base.py b/pandas/core/base.py
index cb8848db9a..2858aef544 100644
--- a/pandas/core/base.py
+++ b/pandas/core/base.py
@@ -18,8 +18,6 @@ import warnings
 
 import numpy as np
 
-from pandas._config import using_copy_on_write
-
 from pandas._libs import lib
 from pandas._typing import (
     AxisInt,
@@ -661,10 +659,10 @@ class IndexOpsMixin(OpsMixin):
 
         result = np.asarray(values, dtype=dtype)
 
-        if (copy and not fillna) or (not copy and using_copy_on_write()):
+        if (copy and not fillna) or not copy:
             if np.shares_memory(self._values[:2], result[:2]):
                 # Take slices to improve performance of check
-                if using_copy_on_write() and not copy:
+                if not copy:
                     result = result.view()
                     result.flags.writeable = False
                 else:
@@ -896,7 +894,7 @@ class IndexOpsMixin(OpsMixin):
         return bool(isna(self).any())  # type: ignore[union-attr]
 
     @final
-    def _map_values(self, mapper, na_action=None, convert: bool = True):
+    def _map_values(self, mapper, na_action=None):
         """
         An internal function that maps values using the input
         correspondence (which can be a dict, Series, or function).
@@ -908,10 +906,6 @@ class IndexOpsMixin(OpsMixin):
         na_action : {None, 'ignore'}
             If 'ignore', propagate NA values, without passing them to the
             mapping function
-        convert : bool, default True
-            Try to find better dtype for elementwise function results. If
-            False, leave as dtype=object. Note that the dtype is always
-            preserved for some extension array dtypes, such as Categorical.
 
         Returns
         -------
@@ -925,7 +919,7 @@ class IndexOpsMixin(OpsMixin):
         if isinstance(arr, ExtensionArray):
             return arr.map(mapper, na_action=na_action)
 
-        return algorithms.map_array(arr, mapper, na_action=na_action, convert=convert)
+        return algorithms.map_array(arr, mapper, na_action=na_action)
 
     @final
     def value_counts(
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
index a62c3c8cd5..0b9d9eba80 100644
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -5545,6 +5545,9 @@ class DataFrame(NDFrame, OpsMixin):
             )
             fill_value = lib.no_default
 
+        if self.empty:
+            return self.copy()
+
         axis = self._get_axis_number(axis)
 
         if is_list_like(periods):
diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py
index b1dfabcf0c..1be1cdfcd0 100644
--- a/pandas/core/groupby/groupby.py
+++ b/pandas/core/groupby/groupby.py
@@ -47,6 +47,7 @@ from pandas._typing import (
     DtypeObj,
     FillnaOptions,
     IndexLabel,
+    IntervalClosedType,
     NDFrameT,
     PositionalIndexer,
     RandomState,
@@ -139,6 +140,7 @@ from pandas.core.util.numba_ import (
 )
 
 if TYPE_CHECKING:
+    from pandas._libs.tslibs import BaseOffset
     from pandas._typing import (
         Any,
         Concatenate,
@@ -147,6 +149,7 @@ if TYPE_CHECKING:
         T,
     )
 
+    from pandas.core.indexers.objects import BaseIndexer
     from pandas.core.resample import Resampler
     from pandas.core.window import (
         ExpandingGroupby,
@@ -3616,7 +3619,16 @@ class GroupBy(BaseGroupBy[NDFrameT]):
         )
 
     @final
-    def rolling(self, *args, **kwargs) -> RollingGroupby:
+    def rolling(
+        self,
+        window: int | datetime.timedelta | str | BaseOffset | BaseIndexer,
+        min_periods: int | None = None,
+        center: bool = False,
+        win_type: str | None = None,
+        on: str | None = None,
+        closed: IntervalClosedType | None = None,
+        method: str = "single",
+    ) -> RollingGroupby:
         """
         Return a rolling grouper, providing rolling functionality per group.
 
@@ -3746,10 +3758,15 @@ class GroupBy(BaseGroupBy[NDFrameT]):
 
         return RollingGroupby(
             self._selected_obj,
-            *args,
+            window=window,
+            min_periods=min_periods,
+            center=center,
+            win_type=win_type,
+            on=on,
+            closed=closed,
+            method=method,
             _grouper=self._grouper,
             _as_index=self.as_index,
-            **kwargs,
         )
 
     @final
diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py
index 15292953e7..a8887a21af 100644
--- a/pandas/core/indexes/api.py
+++ b/pandas/core/indexes/api.py
@@ -74,7 +74,6 @@ def get_objs_combined_axis(
     intersect: bool = False,
     axis: Axis = 0,
     sort: bool = True,
-    copy: bool = False,
 ) -> Index:
     """
     Extract combined index: return intersection or union (depending on the
@@ -92,15 +91,13 @@ def get_objs_combined_axis(
         The axis to extract indexes from.
     sort : bool, default True
         Whether the result index should come out sorted or not.
-    copy : bool, default False
-        If True, return a copy of the combined index.
 
     Returns
     -------
     Index
     """
     obs_idxes = [obj._get_axis(axis) for obj in objs]
-    return _get_combined_index(obs_idxes, intersect=intersect, sort=sort, copy=copy)
+    return _get_combined_index(obs_idxes, intersect=intersect, sort=sort)
 
 
 def _get_distinct_objs(objs: list[Index]) -> list[Index]:
@@ -121,7 +118,6 @@ def _get_combined_index(
     indexes: list[Index],
     intersect: bool = False,
     sort: bool = False,
-    copy: bool = False,
 ) -> Index:
     """
     Return the union or intersection of indexes.
@@ -135,8 +131,6 @@ def _get_combined_index(
         calculate the union.
     sort : bool, default False
         Whether the result index should come out sorted or not.
-    copy : bool, default False
-        If True, return a copy of the combined index.
 
     Returns
     -------
@@ -158,10 +152,6 @@ def _get_combined_index(
 
     if sort:
         index = safe_sort_index(index)
-    # GH 29879
-    if copy:
-        index = index.copy()
-
     return index
 
 
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
index e2b3666ea9..d9f545969d 100644
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -22,7 +22,6 @@ import numpy as np
 
 from pandas._config import (
     get_option,
-    using_copy_on_write,
     using_pyarrow_string_dtype,
 )
 
@@ -1633,7 +1632,7 @@ class Index(IndexOpsMixin, PandasObject):
 
         if name is lib.no_default:
             name = self._get_level_names()
-        result = DataFrame({name: self}, copy=not using_copy_on_write())
+        result = DataFrame({name: self}, copy=False)
 
         if index:
             result.index = self
@@ -4773,13 +4772,11 @@ class Index(IndexOpsMixin, PandasObject):
         [(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]]
         Length: 5, dtype: interval[int64, right]
         """
-        if using_copy_on_write():
-            data = self._data
-            if isinstance(data, np.ndarray):
-                data = data.view()
-                data.flags.writeable = False
-            return data
-        return self._data
+        data = self._data
+        if isinstance(data, np.ndarray):
+            data = data.view()
+            data.flags.writeable = False
+        return data
 
     @cache_readonly
     @doc(IndexOpsMixin.array)
diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py
index 17a8bcd702..8242dc49e6 100644
--- a/pandas/core/indexes/datetimelike.py
+++ b/pandas/core/indexes/datetimelike.py
@@ -16,8 +16,6 @@ from typing import (
 
 import numpy as np
 
-from pandas._config import using_copy_on_write
-
 from pandas._libs import (
     NaT,
     Timedelta,
@@ -454,9 +452,8 @@ class DatetimeTimedeltaMixin(DatetimeIndexOpsMixin, ABC):
     def values(self) -> np.ndarray:
         # NB: For Datetime64TZ this is lossy
         data = self._data._ndarray
-        if using_copy_on_write():
-            data = data.view()
-            data.flags.writeable = False
+        data = data.view()
+        data.flags.writeable = False
         return data
 
     @doc(DatetimeIndexOpsMixin.shift)
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
index 7d31467c6f..c6759a9f54 100644
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -13,8 +13,6 @@ import warnings
 
 import numpy as np
 
-from pandas._config import using_copy_on_write
-
 from pandas._libs.indexing import NDFrameIndexerBase
 from pandas._libs.lib import item_from_zerodim
 from pandas.compat import PYPY
@@ -894,7 +892,7 @@ class _LocationIndexer(NDFrameIndexerBase):
 
     @final
     def __setitem__(self, key, value) -> None:
-        if not PYPY and using_copy_on_write():
+        if not PYPY:
             if sys.getrefcount(self.obj) <= 2:
                 warnings.warn(
                     _chained_assignment_msg, ChainedAssignmentError, stacklevel=2
@@ -2107,7 +2105,7 @@ class _iLocIndexer(_LocationIndexer):
                         tuple(sub_indexer),
                         value[item],
                         multiindex_indexer,
-                        using_cow=using_copy_on_write(),
+                        using_cow=True,
                     )
                 else:
                     val = np.nan
diff --git a/pandas/core/reshape/concat.py b/pandas/core/reshape/concat.py
index d47cb3f40f..88323e5304 100644
--- a/pandas/core/reshape/concat.py
+++ b/pandas/core/reshape/concat.py
@@ -15,8 +15,6 @@ import warnings
 
 import numpy as np
 
-from pandas._config import using_copy_on_write
-
 from pandas.util._decorators import cache_readonly
 from pandas.util._exceptions import find_stack_level
 
@@ -370,13 +368,6 @@ def concat(
     0   1   2
     1   3   4
     """
-    if copy is None:
-        if using_copy_on_write():
-            copy = False
-        else:
-            copy = True
-    elif copy and using_copy_on_write():
-        copy = False
 
     op = _Concatenator(
         objs,
@@ -387,7 +378,6 @@ def concat(
         levels=levels,
         names=names,
         verify_integrity=verify_integrity,
-        copy=copy,
         sort=sort,
     )
 
@@ -411,7 +401,6 @@ class _Concatenator:
         names: list[HashableT] | None = None,
         ignore_index: bool = False,
         verify_integrity: bool = False,
-        copy: bool = True,
         sort: bool = False,
     ) -> None:
         if isinstance(objs, (ABCSeries, ABCDataFrame, str)):
@@ -439,7 +428,6 @@ class _Concatenator:
 
         self.ignore_index = ignore_index
         self.verify_integrity = verify_integrity
-        self.copy = copy
 
         objs, keys = self._clean_keys_and_objs(objs, keys)
 
@@ -656,7 +644,7 @@ class _Concatenator:
                 cons = sample._constructor_expanddim
 
                 index, columns = self.new_axes
-                df = cons(data, index=index, copy=self.copy)
+                df = cons(data, index=index, copy=False)
                 df.columns = columns
                 return df.__finalize__(self, method="concat")
 
@@ -681,10 +669,8 @@ class _Concatenator:
                 mgrs_indexers.append((obj._mgr, indexers))
 
             new_data = concatenate_managers(
-                mgrs_indexers, self.new_axes, concat_axis=self.bm_axis, copy=self.copy
+                mgrs_indexers, self.new_axes, concat_axis=self.bm_axis, copy=False
             )
-            if not self.copy and not using_copy_on_write():
-                new_data._consolidate_inplace()
 
             out = sample._constructor_from_mgr(new_data, axes=new_data.axes)
             return out.__finalize__(self, method="concat")
@@ -710,7 +696,6 @@ class _Concatenator:
             axis=data_axis,
             intersect=self.intersect,
             sort=self.sort,
-            copy=self.copy,
         )
 
     @cache_readonly
diff --git a/pandas/core/series.py b/pandas/core/series.py
index d5eaa2125b..92e9ebaefd 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -4481,7 +4481,6 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
     def apply(
         self,
         func: AggFuncType,
-        convert_dtype: bool | lib.NoDefault = lib.no_default,
         args: tuple[Any, ...] = (),
         *,
         by_row: Literal[False, "compat"] = "compat",
@@ -4497,14 +4496,6 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
         ----------
         func : function
             Python function or NumPy ufunc to apply.
-        convert_dtype : bool, default True
-            Try to find better dtype for elementwise function results. If
-            False, leave as dtype=object. Note that the dtype is always
-            preserved for some extension array dtypes, such as Categorical.
-
-            .. deprecated:: 2.1.0
-                ``convert_dtype`` has been deprecated. Do ``ser.astype(object).apply()``
-                instead if you want ``convert_dtype=False``.
         args : tuple
             Positional arguments passed to func after the series value.
         by_row : False or "compat", default "compat"
@@ -4608,7 +4599,6 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
         return SeriesApply(
             self,
             func,
-            convert_dtype=convert_dtype,
             by_row=by_row,
             args=args,
             kwargs=kwargs,
diff --git a/pandas/io/parsers/readers.py b/pandas/io/parsers/readers.py
index 07920eb175..d9dc5b41a8 100644
--- a/pandas/io/parsers/readers.py
+++ b/pandas/io/parsers/readers.py
@@ -26,8 +26,6 @@ import warnings
 
 import numpy as np
 
-from pandas._config import using_copy_on_write
-
 from pandas._libs import lib
 from pandas._libs.parsers import STR_NA_VALUES
 from pandas.errors import (
@@ -1967,7 +1965,7 @@ class TextFileReader(abc.Iterator):
                 new_col_dict,
                 columns=columns,
                 index=index,
-                copy=not using_copy_on_write(),
+                copy=False,
             )
 
             self._currow += new_rows
diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py
index 1e11a9783f..97b9b905df 100644
--- a/pandas/io/pytables.py
+++ b/pandas/io/pytables.py
@@ -30,7 +30,6 @@ import numpy as np
 from pandas._config import (
     config,
     get_option,
-    using_copy_on_write,
     using_pyarrow_string_dtype,
 )
 
@@ -3294,13 +3293,8 @@ class BlockManagerFixed(GenericFixed):
             dfs.append(df)
 
         if len(dfs) > 0:
-            out = concat(dfs, axis=1, copy=True)
-            if using_copy_on_write():
-                # with CoW, concat ignores the copy keyword. Here, we still want
-                # to copy to enforce optimized column-major layout
-                out = out.copy()
-            out = out.reindex(columns=items, copy=False)
-            return out
+            out = concat(dfs, axis=1).copy()
+            return out.reindex(columns=items, copy=False)
 
         return DataFrame(columns=axes[0], index=axes[1])
 


Test patch:
diff --git a/pandas/_testing/asserters.py b/pandas/_testing/asserters.py
index 4aea85d50c..3aacd3099c 100644
--- a/pandas/_testing/asserters.py
+++ b/pandas/_testing/asserters.py
@@ -902,6 +902,7 @@ def assert_series_equal(
     >>> tm.assert_series_equal(a, b)
     """
     __tracebackhide__ = True
+    check_exact_index = False if check_exact is lib.no_default else check_exact
     if (
         check_exact is lib.no_default
         and rtol is lib.no_default
@@ -944,7 +945,7 @@ def assert_series_equal(
             right.index,
             exact=check_index_type,
             check_names=check_names,
-            check_exact=check_exact,
+            check_exact=check_exact_index,
             check_categorical=check_categorical,
             check_order=not check_like,
             rtol=rtol,
diff --git a/pandas/tests/apply/test_series_apply.py b/pandas/tests/apply/test_series_apply.py
index 1e41e54bff..cd1547620e 100644
--- a/pandas/tests/apply/test_series_apply.py
+++ b/pandas/tests/apply/test_series_apply.py
@@ -75,17 +75,6 @@ def test_apply_map_same_length_inference_bug():
     tm.assert_series_equal(result, expected)
 
 
-@pytest.mark.parametrize("convert_dtype", [True, False])
-def test_apply_convert_dtype_deprecated(convert_dtype):
-    ser = Series(np.random.default_rng(2).standard_normal(10))
-
-    def func(x):
-        return x if x > 0 else np.nan
-
-    with tm.assert_produces_warning(FutureWarning):
-        ser.apply(func, convert_dtype=convert_dtype, by_row="compat")
-
-
 def test_apply_args():
     s = Series(["foo,bar"])
 
diff --git a/pandas/tests/frame/methods/test_asfreq.py b/pandas/tests/frame/methods/test_asfreq.py
index 87d1745774..f6b71626b6 100644
--- a/pandas/tests/frame/methods/test_asfreq.py
+++ b/pandas/tests/frame/methods/test_asfreq.py
@@ -8,6 +8,7 @@ from pandas._libs.tslibs.offsets import MonthEnd
 from pandas import (
     DataFrame,
     DatetimeIndex,
+    PeriodIndex,
     Series,
     date_range,
     period_range,
@@ -257,3 +258,28 @@ class TestAsFreq:
         with tm.assert_produces_warning(FutureWarning, match=depr_msg):
             result = df.asfreq(freq=freq_depr)
         tm.assert_frame_equal(result, expected)
+
+    @pytest.mark.parametrize(
+        "freq, error_msg",
+        [
+            (
+                "2MS",
+                "MS is not supported as period frequency",
+            ),
+            (
+                offsets.MonthBegin(),
+                r"\<MonthBegin\> is not supported as period frequency",
+            ),
+            (
+                offsets.DateOffset(months=2),
+                r"\<DateOffset: months=2\> is not supported as period frequency",
+            ),
+        ],
+    )
+    def test_asfreq_unsupported_freq(self, freq, error_msg):
+        # https://github.com/pandas-dev/pandas/issues/56718
+        index = PeriodIndex(["2020-01-01", "2021-01-01"], freq="M")
+        df = DataFrame({"a": Series([0, 1], index=index)})
+
+        with pytest.raises(ValueError, match=error_msg):
+            df.asfreq(freq=freq)
diff --git a/pandas/tests/frame/methods/test_shift.py b/pandas/tests/frame/methods/test_shift.py
index c477c9c185..5bcfaf8b19 100644
--- a/pandas/tests/frame/methods/test_shift.py
+++ b/pandas/tests/frame/methods/test_shift.py
@@ -751,3 +751,9 @@ class TestDataFrameShift:
         msg = "Cannot specify `suffix` if `periods` is an int."
         with pytest.raises(ValueError, match=msg):
             df.shift(1, suffix="fails")
+
+    def test_shift_axis_one_empty(self):
+        # GH#57301
+        df = DataFrame()
+        result = df.shift(1, axis=1)
+        tm.assert_frame_equal(result, df)
diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py
index db45067162..2d06c2f258 100644
--- a/pandas/tests/groupby/test_groupby.py
+++ b/pandas/tests/groupby/test_groupby.py
@@ -2423,7 +2423,9 @@ def test_rolling_wrong_param_min_period():
     test_df = DataFrame([name_l, val_l]).T
     test_df.columns = ["name", "val"]
 
-    result_error_msg = r"__init__\(\) got an unexpected keyword argument 'min_period'"
+    result_error_msg = (
+        r"^[a-zA-Z._]*\(\) got an unexpected keyword argument 'min_period'$"
+    )
     with pytest.raises(TypeError, match=result_error_msg):
         test_df.groupby("name")["val"].rolling(window=2, min_period=1).sum()
 
diff --git a/pandas/tests/util/test_assert_series_equal.py b/pandas/tests/util/test_assert_series_equal.py
index 4c695190c1..0b3bc07c17 100644
--- a/pandas/tests/util/test_assert_series_equal.py
+++ b/pandas/tests/util/test_assert_series_equal.py
@@ -473,3 +473,11 @@ def test_assert_series_equal_int_tol():
     tm.assert_extension_array_equal(
         left.astype("Int64").values, right.astype("Int64").values, rtol=1.5
     )
+
+
+def test_assert_series_equal_index_exact_default():
+    # GH#57067
+    ser1 = Series(np.zeros(6, dtype=int), [0, 0.2, 0.4, 0.6, 0.8, 1])
+    ser2 = Series(np.zeros(6, dtype=int), np.linspace(0, 1, 6))
+    tm.assert_series_equal(ser1, ser2)
+    tm.assert_frame_equal(ser1.to_frame(), ser2.to_frame())