Repository: pandas-dev/pandas
Pull request #57190 (2024-02-01 05:07:12)

CI: Pinning doc previewer action to current release
I don't think we'll have any doc previewer release that won't be done for pandas, but pinning the action to avoid automatic upgrades anyway.


Patch:
diff --git a/.github/workflows/comment-commands.yml b/.github/workflows/comment-commands.yml
index b24a689078..da9f6ac8bf 100644
--- a/.github/workflows/comment-commands.yml
+++ b/.github/workflows/comment-commands.yml
@@ -24,7 +24,7 @@ jobs:
     concurrency:
       group: ${{ github.actor }}-preview-docs
     steps:
-      - uses: pandas-dev/github-doc-previewer@master
+      - uses: pandas-dev/github-doc-previewer@v0.3.1
         with:
           previewer-server: "https://pandas.pydata.org/preview"
           artifact-job: "Doc Build and Upload"
diff --git a/asv_bench/benchmarks/algos/isin.py b/asv_bench/benchmarks/algos/isin.py
index f9ea3d5684..a17732c70c 100644
--- a/asv_bench/benchmarks/algos/isin.py
+++ b/asv_bench/benchmarks/algos/isin.py
@@ -59,7 +59,8 @@ class IsIn:
         elif dtype in ["str", "string[python]", "string[pyarrow]"]:
             try:
                 self.series = Series(
-                    Index([f"i-{i}" for i in range(N)], dtype=object), dtype=dtype
+                    Index([f"i-{i}" for i in range(N)], dtype=object)._values,
+                    dtype=dtype,
                 )
             except ImportError as err:
                 raise NotImplementedError from err
diff --git a/asv_bench/benchmarks/strings.py b/asv_bench/benchmarks/strings.py
index e6842fbc13..467fab857d 100644
--- a/asv_bench/benchmarks/strings.py
+++ b/asv_bench/benchmarks/strings.py
@@ -19,7 +19,8 @@ class Dtypes:
     def setup(self, dtype):
         try:
             self.s = Series(
-                Index([f"i-{i}" for i in range(10000)], dtype=object), dtype=dtype
+                Index([f"i-{i}" for i in range(10000)], dtype=object)._values,
+                dtype=dtype,
             )
         except ImportError as err:
             raise NotImplementedError from err
diff --git a/pandas/_config/__init__.py b/pandas/_config/__init__.py
index 5b2bac2e8d..0594d1c190 100644
--- a/pandas/_config/__init__.py
+++ b/pandas/_config/__init__.py
@@ -32,13 +32,11 @@ from pandas._config.display import detect_console_encoding
 
 
 def using_copy_on_write() -> bool:
-    _mode_options = _global_config["mode"]
-    return _mode_options["copy_on_write"] is True
+    return True
 
 
 def warn_copy_on_write() -> bool:
-    _mode_options = _global_config["mode"]
-    return _mode_options["copy_on_write"] == "warn"
+    return False
 
 
 def using_nullable_dtypes() -> bool:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index 037f809eaa..73f9481e53 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -6704,8 +6704,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         :ref:`gotchas <gotchas.thread-safety>` when copying in a threading
         environment.
 
-        When ``copy_on_write`` in pandas config is set to ``True``, the
-        ``copy_on_write`` config takes effect even when ``deep=False``.
+        Copy-on-Write protects shallow copies against accidental modifications.
         This means that any changes to the copied data would make a new copy
         of the data upon write (and vice versa). Changes made to either the
         original or copied variable would not be reflected in the counterpart.
@@ -6731,12 +6730,15 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         >>> deep = s.copy()
         >>> shallow = s.copy(deep=False)
 
-        Shallow copy shares data and index with original.
+        Shallow copy shares index with original, the data is a
+        view of the original.
 
         >>> s is shallow
         False
-        >>> s.values is shallow.values and s.index is shallow.index
-        True
+        >>> s.values is shallow.values
+        False
+        >>> s.index is shallow.index
+        False
 
         Deep copy has own copy of data and index.
 
@@ -6745,18 +6747,17 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         >>> s.values is deep.values or s.index is deep.index
         False
 
-        Updates to the data shared by shallow copy and original is reflected
-        in both (NOTE: this will no longer be true for pandas >= 3.0);
-        deep copy remains unchanged.
+        The shallow copy is protected against updating the original object
+        as well. Thus, updates will only reflect in one of both objects.
 
         >>> s.iloc[0] = 3
         >>> shallow.iloc[1] = 4
         >>> s
         a    3
-        b    4
+        b    2
         dtype: int64
         >>> shallow
-        a    3
+        a    1
         b    4
         dtype: int64
         >>> deep
@@ -6779,22 +6780,6 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         0    [10, 2]
         1     [3, 4]
         dtype: object
-
-        **Copy-on-Write is set to true**, the shallow copy is not modified
-        when the original data is changed:
-
-        >>> with pd.option_context("mode.copy_on_write", True):
-        ...     s = pd.Series([1, 2], index=["a", "b"])
-        ...     copy = s.copy(deep=False)
-        ...     s.iloc[0] = 100
-        ...     s
-        a    100
-        b      2
-        dtype: int64
-        >>> copy
-        a    1
-        b    2
-        dtype: int64
         """
         data = self._mgr.copy(deep=deep)
         self._clear_item_cache()
diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py
index a80ee421a1..0495f23508 100644
--- a/pandas/core/indexes/multi.py
+++ b/pandas/core/indexes/multi.py
@@ -3492,6 +3492,8 @@ class MultiIndex(Index):
                         "cannot index with a boolean indexer that "
                         "is not the same length as the index"
                     )
+                if isinstance(k, (ABCSeries, Index)):
+                    k = k._values
                 lvl_indexer = np.asarray(k)
                 if indexer is None:
                     lvl_indexer = lvl_indexer.copy()


Test patch:
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index a3cffb4b03..2b09aa9343 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -73,18 +73,6 @@ jobs:
             env_file: actions-312.yaml
             pattern: "not slow and not network and not single_cpu"
             pandas_copy_on_write: "1"
-          - name: "Copy-on-Write 3.11 (warnings)"
-            env_file: actions-311.yaml
-            pattern: "not slow and not network and not single_cpu"
-            pandas_copy_on_write: "warn"
-          - name: "Copy-on-Write 3.10 (warnings)"
-            env_file: actions-310.yaml
-            pattern: "not slow and not network and not single_cpu"
-            pandas_copy_on_write: "warn"
-          - name: "Copy-on-Write 3.9 (warnings)"
-            env_file: actions-39.yaml
-            pattern: "not slow and not network and not single_cpu"
-            pandas_copy_on_write: "warn"
           - name: "Pypy"
             env_file: actions-pypy-39.yaml
             pattern: "not slow and not network and not single_cpu"
diff --git a/pandas/conftest.py b/pandas/conftest.py
index 26e03ca30d..db251a07ae 100644
--- a/pandas/conftest.py
+++ b/pandas/conftest.py
@@ -1963,7 +1963,7 @@ def using_copy_on_write() -> bool:
     """
     Fixture to check if Copy-on-Write is enabled.
     """
-    return pd.options.mode.copy_on_write is True
+    return True
 
 
 @pytest.fixture
@@ -1971,7 +1971,7 @@ def warn_copy_on_write() -> bool:
     """
     Fixture to check if Copy-on-Write is in warning mode.
     """
-    return pd.options.mode.copy_on_write == "warn"
+    return False
 
 
 @pytest.fixture
diff --git a/pandas/tests/copy_view/test_internals.py b/pandas/tests/copy_view/test_internals.py
index 400fb8e03c..f1a4decce6 100644
--- a/pandas/tests/copy_view/test_internals.py
+++ b/pandas/tests/copy_view/test_internals.py
@@ -1,7 +1,6 @@
 import numpy as np
 import pytest
 
-import pandas as pd
 from pandas import DataFrame
 import pandas._testing as tm
 from pandas.tests.copy_view.util import get_array
@@ -42,35 +41,6 @@ def test_consolidate(using_copy_on_write):
         assert df.loc[0, "b"] == 0.1
 
 
-@pytest.mark.single_cpu
-def test_switch_options():
-    # ensure we can switch the value of the option within one session
-    # (assuming data is constructed after switching)
-
-    # using the option_context to ensure we set back to global option value
-    # after running the test
-    with pd.option_context("mode.copy_on_write", False):
-        df = DataFrame({"a": [1, 2, 3], "b": [0.1, 0.2, 0.3]})
-        subset = df[:]
-        subset.iloc[0, 0] = 0
-        # df updated with CoW disabled
-        assert df.iloc[0, 0] == 0
-
-        pd.options.mode.copy_on_write = True
-        df = DataFrame({"a": [1, 2, 3], "b": [0.1, 0.2, 0.3]})
-        subset = df[:]
-        subset.iloc[0, 0] = 0
-        # df not updated with CoW enabled
-        assert df.iloc[0, 0] == 1
-
-        pd.options.mode.copy_on_write = False
-        df = DataFrame({"a": [1, 2, 3], "b": [0.1, 0.2, 0.3]})
-        subset = df[:]
-        subset.iloc[0, 0] = 0
-        # df updated with CoW disabled
-        assert df.iloc[0, 0] == 0
-
-
 @pytest.mark.parametrize("dtype", [np.intp, np.int8])
 @pytest.mark.parametrize(
     "locs, arr",
diff --git a/pandas/tests/extension/conftest.py b/pandas/tests/extension/conftest.py
index 77f1dd2a8e..3a3844d5a8 100644
--- a/pandas/tests/extension/conftest.py
+++ b/pandas/tests/extension/conftest.py
@@ -2,10 +2,7 @@ import operator
 
 import pytest
 
-from pandas import (
-    Series,
-    options,
-)
+from pandas import Series
 
 
 @pytest.fixture
@@ -222,4 +219,4 @@ def using_copy_on_write() -> bool:
     """
     Fixture to check if Copy-on-Write is enabled.
     """
-    return options.mode.copy_on_write is True
+    return True
diff --git a/pandas/tests/frame/methods/test_copy.py b/pandas/tests/frame/methods/test_copy.py
index 6208d0256a..5b72a84320 100644
--- a/pandas/tests/frame/methods/test_copy.py
+++ b/pandas/tests/frame/methods/test_copy.py
@@ -1,10 +1,7 @@
 import numpy as np
 import pytest
 
-import pandas.util._test_decorators as td
-
 from pandas import DataFrame
-import pandas._testing as tm
 
 
 class TestCopy:
@@ -18,25 +15,6 @@ class TestCopy:
         getattr(cp, attr).name = "foo"
         assert getattr(float_frame, attr).name is None
 
-    @td.skip_copy_on_write_invalid_test
-    def test_copy_cache(self):
-        # GH#31784 _item_cache not cleared on copy causes incorrect reads after updates
-        df = DataFrame({"a": [1]})
-
-        df["x"] = [0]
-        df["a"]
-
-        df.copy()
-
-        df["a"].values[0] = -1
-
-        tm.assert_frame_equal(df, DataFrame({"a": [-1], "x": [0]}))
-
-        df["y"] = [0]
-
-        assert df["a"].values[0] == -1
-        tm.assert_frame_equal(df, DataFrame({"a": [-1], "x": [0], "y": [0]}))
-
     def test_copy(self, float_frame, float_string_frame):
         cop = float_frame.copy()
         cop["E"] = cop["A"]
diff --git a/pandas/tests/series/test_ufunc.py b/pandas/tests/series/test_ufunc.py
index 3ef3191743..94a6910509 100644
--- a/pandas/tests/series/test_ufunc.py
+++ b/pandas/tests/series/test_ufunc.py
@@ -5,8 +5,6 @@ import string
 import numpy as np
 import pytest
 
-import pandas.util._test_decorators as td
-
 import pandas as pd
 import pandas._testing as tm
 from pandas.arrays import SparseArray
@@ -456,8 +454,7 @@ def test_array_ufuncs_for_many_arguments():
         ufunc(ser, ser, df)
 
 
-# TODO(CoW) see https://github.com/pandas-dev/pandas/pull/51082
-@td.skip_copy_on_write_not_yet_implemented
+@pytest.mark.xfail(reason="see https://github.com/pandas-dev/pandas/pull/51082")
 def test_np_fix():
     # np.fix is not a ufunc but is composed of several ufunc calls under the hood
     # with `out` and `where` keywords
diff --git a/pandas/tests/test_downstream.py b/pandas/tests/test_downstream.py
index 10776fe5d0..feba0e86c6 100644
--- a/pandas/tests/test_downstream.py
+++ b/pandas/tests/test_downstream.py
@@ -58,8 +58,8 @@ def test_dask_ufunc():
         s = Series([1.5, 2.3, 3.7, 4.0])
         ds = dd.from_pandas(s, npartitions=2)
 
-        result = da.fix(ds).compute()
-        expected = np.fix(s)
+        result = da.log(ds).compute()
+        expected = np.log(s)
         tm.assert_series_equal(result, expected)
     finally:
         pd.set_option("compute.use_numexpr", olduse)
diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py
index 37908c9ac2..7862678128 100644
--- a/pandas/util/_test_decorators.py
+++ b/pandas/util/_test_decorators.py
@@ -33,12 +33,9 @@ from typing import (
 
 import pytest
 
-from pandas._config import get_option
-
 if TYPE_CHECKING:
     from pandas._typing import F
 
-
 from pandas.compat import (
     IS64,
     is_platform_windows,
@@ -144,14 +141,3 @@ def parametrize_fixture_doc(*args) -> Callable[[F], F]:
         return fixture
 
     return documented_fixture
-
-
-skip_copy_on_write_not_yet_implemented = pytest.mark.xfail(
-    get_option("mode.copy_on_write") is True,
-    reason="Not yet implemented/adapted for Copy-on-Write mode",
-)
-
-skip_copy_on_write_invalid_test = pytest.mark.skipif(
-    get_option("mode.copy_on_write") is True,
-    reason="Test not valid for Copy-on-Write mode",
-)