diff --git a/pandas/_libs/internals.pyi b/pandas/_libs/internals.pyi index 1064995a51797..11d059ec53920 100644 --- a/pandas/_libs/internals.pyi +++ b/pandas/_libs/internals.pyi @@ -94,7 +94,3 @@ class BlockValuesRefs: def add_reference(self, blk: Block) -> None: ... def add_index_reference(self, index: Index) -> None: ... def has_reference(self) -> bool: ... - -class SetitemMixin: - def __setitem__(self, key, value) -> None: ... - def __delitem__(self, key) -> None: ... diff --git a/pandas/_libs/internals.pyx b/pandas/_libs/internals.pyx index c651743895995..4fb24c9ad1538 100644 --- a/pandas/_libs/internals.pyx +++ b/pandas/_libs/internals.pyx @@ -1,9 +1,6 @@ from collections import defaultdict -import sys -import warnings cimport cython -from cpython cimport PY_VERSION_HEX from cpython.object cimport PyObject from cpython.pyport cimport PY_SSIZE_T_MAX from cpython.slice cimport PySlice_GetIndicesEx @@ -23,9 +20,6 @@ from numpy cimport ( cnp.import_array() from pandas._libs.algos import ensure_int64 -from pandas.compat import CHAINED_WARNING_DISABLED -from pandas.errors import ChainedAssignmentError -from pandas.errors.cow import _chained_assignment_msg from pandas._libs.util cimport ( is_array, @@ -1002,47 +996,3 @@ cdef class BlockValuesRefs: return self._has_reference_maybe_locked() ELSE: return self._has_reference_maybe_locked() - - -cdef extern from "Python.h": - """ - // python version < 3.14 - #if PY_VERSION_HEX < 0x030E0000 - // This function is unused and is declared to avoid a build warning - int __Pyx_PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *ref) { - return Py_REFCNT(ref) == 1; - } - #else - #define __Pyx_PyUnstable_Object_IsUniqueReferencedTemporary \ - PyUnstable_Object_IsUniqueReferencedTemporary - #endif - """ - int PyUnstable_Object_IsUniqueReferencedTemporary\ - "__Pyx_PyUnstable_Object_IsUniqueReferencedTemporary"(object o) except -1 - - -# Python version compatibility for PyUnstable_Object_IsUniqueReferencedTemporary -cdef inline bint _is_unique_referenced_temporary(object obj) except -1: - if PY_VERSION_HEX >= 0x030E0000: - # Python 3.14+ has PyUnstable_Object_IsUniqueReferencedTemporary - return PyUnstable_Object_IsUniqueReferencedTemporary(obj) - else: - # Fallback for older Python versions using sys.getrefcount - return sys.getrefcount(obj) <= 1 - - -cdef class SetitemMixin: - # class used in DataFrame and Series for checking for chained assignment - - def __setitem__(self, key, value) -> None: - cdef bint is_unique = 0 - if not CHAINED_WARNING_DISABLED: - is_unique = _is_unique_referenced_temporary(self) - if is_unique: - warnings.warn( - _chained_assignment_msg, ChainedAssignmentError, stacklevel=1 - ) - self._setitem(key, value) - - def __delitem__(self, key) -> None: - self._delitem(key) diff --git a/pandas/_testing/contexts.py b/pandas/_testing/contexts.py index 8bbd20c742c9c..c92a63b625a39 100644 --- a/pandas/_testing/contexts.py +++ b/pandas/_testing/contexts.py @@ -12,10 +12,7 @@ ) import uuid -from pandas.compat import ( - CHAINED_WARNING_DISABLED, - CHAINED_WARNING_DISABLED_INPLACE_METHOD, -) +from pandas.compat import CHAINED_WARNING_DISABLED from pandas.errors import ChainedAssignmentError from pandas.io.common import get_handle @@ -163,18 +160,10 @@ def with_csv_dialect(name: str, **kwargs) -> Generator[None]: csv.unregister_dialect(name) -def raises_chained_assignment_error( - extra_warnings=(), extra_match=(), inplace_method=False -): +def raises_chained_assignment_error(extra_warnings=(), extra_match=()): from pandas._testing import assert_produces_warning - WARNING_DISABLED = ( - CHAINED_WARNING_DISABLED_INPLACE_METHOD - if inplace_method - else CHAINED_WARNING_DISABLED - ) - - if WARNING_DISABLED: + if CHAINED_WARNING_DISABLED: if not extra_warnings: from contextlib import nullcontext diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index 72d9c2555d16e..49b56c6396155 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -17,7 +17,6 @@ from pandas.compat._constants import ( CHAINED_WARNING_DISABLED, - CHAINED_WARNING_DISABLED_INPLACE_METHOD, IS64, ISMUSL, PY312, @@ -154,7 +153,6 @@ def is_ci_environment() -> bool: __all__ = [ "CHAINED_WARNING_DISABLED", - "CHAINED_WARNING_DISABLED_INPLACE_METHOD", "HAS_PYARROW", "IS64", "ISMUSL", diff --git a/pandas/compat/_constants.py b/pandas/compat/_constants.py index 102f6fef6e4e1..8ad31e0725bd4 100644 --- a/pandas/compat/_constants.py +++ b/pandas/compat/_constants.py @@ -18,9 +18,11 @@ PYPY = platform.python_implementation() == "PyPy" WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"]) ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "") -REF_COUNT = 2 -CHAINED_WARNING_DISABLED = PYPY or (PY314 and not sys._is_gil_enabled()) # type: ignore[attr-defined] -CHAINED_WARNING_DISABLED_INPLACE_METHOD = PYPY or PY314 +# the refcount for self in a chained __setitem__/.(i)loc indexing/method call +REF_COUNT = 2 if PY314 else 3 +REF_COUNT_IDX = 2 +REF_COUNT_METHOD = 1 if PY314 else 2 +CHAINED_WARNING_DISABLED = PYPY __all__ = [ diff --git a/pandas/compat/pickle_compat.py b/pandas/compat/pickle_compat.py index 8247356f25f4d..beb4a69232b27 100644 --- a/pandas/compat/pickle_compat.py +++ b/pandas/compat/pickle_compat.py @@ -22,7 +22,6 @@ PeriodArray, TimedeltaArray, ) -from pandas.core.generic import NDFrame from pandas.core.internals import BlockManager if TYPE_CHECKING: @@ -91,10 +90,6 @@ def load_reduce(self) -> None: cls = args[0] stack[-1] = NDArrayBacked.__new__(*args) return - elif args and issubclass(args[0], NDFrame): - cls = args[0] - stack[-1] = cls.__new__(cls) - return raise dispatch[pickle.REDUCE[0]] = load_reduce # type: ignore[assignment] diff --git a/pandas/core/common.py b/pandas/core/common.py index 4f1c8d1800c00..b6aab3ddfee63 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -22,6 +22,7 @@ import contextlib from functools import partial import inspect +import sys from typing import ( TYPE_CHECKING, Any, @@ -650,3 +651,29 @@ def fill_missing_names(names: Sequence[Hashable | None]) -> list[Hashable]: list of column names with the None values replaced. """ return [f"level_{i}" if name is None else name for i, name in enumerate(names)] + + +def is_local_in_caller_frame(obj): + """ + Helper function used in detecting chained assignment. + + If the pandas object (DataFrame/Series) is a local variable + in the caller's frame, it should not be a case of chained + assignment or method call. + + For example: + + def test(): + df = pd.DataFrame(...) + df["a"] = 1 # not chained assignment + + Inside ``df.__setitem__``, we call this function to check whether `df` + (`self`) is a local variable in `test` frame (the frame calling setitem). If + so, we know it is not a case of chained assignment (even when the refcount + of `df` is below the threshold due to optimization of local variables). + """ + frame = sys._getframe(2) + for v in frame.f_locals.values(): + if v is obj: + return True + return False diff --git a/pandas/core/frame.py b/pandas/core/frame.py index c8c246434f6d8..cab6f3702d805 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -48,11 +48,11 @@ properties, ) from pandas._libs.hashtable import duplicated -from pandas._libs.internals import SetitemMixin from pandas._libs.lib import is_range_indexer +from pandas.compat import CHAINED_WARNING_DISABLED from pandas.compat._constants import ( - CHAINED_WARNING_DISABLED_INPLACE_METHOD, REF_COUNT, + REF_COUNT_METHOD, ) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv @@ -63,6 +63,7 @@ ) from pandas.errors.cow import ( _chained_assignment_method_msg, + _chained_assignment_msg, ) from pandas.util._decorators import ( Appender, @@ -520,7 +521,7 @@ @set_module("pandas") -class DataFrame(SetitemMixin, NDFrame, OpsMixin): +class DataFrame(NDFrame, OpsMixin): """ Two-dimensional, size-mutable, potentially heterogeneous tabular data. @@ -667,11 +668,6 @@ class DataFrame(SetitemMixin, NDFrame, OpsMixin): # and ExtensionArray. Should NOT be overridden by subclasses. __pandas_priority__ = 4000 - # override those to avoid inheriting from SetitemMixin (cython generates - # them by default) - __reduce__ = object.__reduce__ - __setstate__ = NDFrame.__setstate__ - @property def _constructor(self) -> type[DataFrame]: return DataFrame @@ -4325,8 +4321,7 @@ def isetitem(self, loc, value) -> None: arraylike, refs = self._sanitize_column(value) self._iset_item_mgr(loc, arraylike, inplace=False, refs=refs) - # def __setitem__() is implemented in SetitemMixin and dispatches to this method - def _setitem(self, key, value) -> None: + def __setitem__(self, key, value) -> None: """ Set item(s) in DataFrame by key. @@ -4410,6 +4405,14 @@ def _setitem(self, key, value) -> None: z 3 50 # Values for 'a' and 'b' are completely ignored! """ + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount(self) <= REF_COUNT and not com.is_local_in_caller_frame( + self + ): + warnings.warn( + _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 + ) + key = com.apply_if_callable(key, self) # see if we can slice the rows @@ -9362,8 +9365,10 @@ def update( 1 2 500.0 2 3 6.0 """ - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not com.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 940231233e308..2669546d52af2 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -82,9 +82,9 @@ WriteExcelBuffer, npt, ) +from pandas.compat import CHAINED_WARNING_DISABLED from pandas.compat._constants import ( - CHAINED_WARNING_DISABLED_INPLACE_METHOD, - REF_COUNT, + REF_COUNT_METHOD, ) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv @@ -2074,6 +2074,7 @@ def __getstate__(self) -> dict[str, Any]: **meta, } + @final def __setstate__(self, state) -> None: if isinstance(state, BlockManager): self._mgr = state @@ -4264,9 +4265,8 @@ def _slice(self, slobj: slice, axis: AxisInt = 0) -> Self: result = result.__finalize__(self) return result - # def __delitem__() is implemented in SetitemMixin and dispatches to this method @final - def _delitem(self, key) -> None: + def __delitem__(self, key) -> None: """ Delete item """ @@ -7081,8 +7081,10 @@ def fillna( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, @@ -7328,8 +7330,10 @@ def ffill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, @@ -7468,8 +7472,10 @@ def bfill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, @@ -7553,8 +7559,10 @@ def replace( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, @@ -7916,8 +7924,10 @@ def interpolate( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, @@ -8571,8 +8581,10 @@ def clip( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, @@ -10206,8 +10218,10 @@ def where( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, @@ -10270,8 +10284,10 @@ def mask( ) -> Self | None: inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not common.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 6487bd449f222..5cda56b9956ea 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -15,10 +15,8 @@ from pandas._libs.indexing import NDFrameIndexerBase from pandas._libs.lib import item_from_zerodim -from pandas.compat._constants import ( - CHAINED_WARNING_DISABLED, - REF_COUNT, -) +from pandas.compat import CHAINED_WARNING_DISABLED +from pandas.compat._constants import REF_COUNT_IDX from pandas.errors import ( AbstractMethodError, ChainedAssignmentError, @@ -920,7 +918,7 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None) -> None: @final def __setitem__(self, key, value) -> None: if not CHAINED_WARNING_DISABLED: - if sys.getrefcount(self.obj) <= REF_COUNT: + if sys.getrefcount(self.obj) <= REF_COUNT_IDX: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) @@ -2588,7 +2586,7 @@ def __getitem__(self, key): def __setitem__(self, key, value) -> None: if not CHAINED_WARNING_DISABLED: - if sys.getrefcount(self.obj) <= REF_COUNT: + if sys.getrefcount(self.obj) <= REF_COUNT_IDX: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) @@ -2619,7 +2617,7 @@ def _convert_key(self, key): def __setitem__(self, key, value) -> None: if not CHAINED_WARNING_DISABLED: - if sys.getrefcount(self.obj) <= REF_COUNT: + if sys.getrefcount(self.obj) <= REF_COUNT_IDX: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) diff --git a/pandas/core/series.py b/pandas/core/series.py index 6d7e713b7ad6b..89c59b6788cd2 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -33,11 +33,11 @@ properties, reshape, ) -from pandas._libs.internals import SetitemMixin from pandas._libs.lib import is_range_indexer +from pandas.compat import CHAINED_WARNING_DISABLED from pandas.compat._constants import ( - CHAINED_WARNING_DISABLED_INPLACE_METHOD, REF_COUNT, + REF_COUNT_METHOD, ) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv @@ -48,6 +48,7 @@ ) from pandas.errors.cow import ( _chained_assignment_method_msg, + _chained_assignment_msg, ) from pandas.util._decorators import ( Appender, @@ -231,7 +232,7 @@ # class "NDFrame") # definition in base class "NDFrame" @set_module("pandas") -class Series(SetitemMixin, base.IndexOpsMixin, NDFrame): # type: ignore[misc] +class Series(base.IndexOpsMixin, NDFrame): # type: ignore[misc] """ One-dimensional ndarray with axis labels (including time series). @@ -357,11 +358,6 @@ class Series(SetitemMixin, base.IndexOpsMixin, NDFrame): # type: ignore[misc] ) _mgr: SingleBlockManager - # override those to avoid inheriting from SetitemMixin (cython generates - # them by default) - __reduce__ = object.__reduce__ - __setstate__ = NDFrame.__setstate__ - # ---------------------------------------------------------------------- # Constructors @@ -1061,8 +1057,15 @@ def _get_value(self, label, takeable: bool = False): else: return self.iloc[loc] - # def __setitem__() is implemented in SetitemMixin and dispatches to this method - def _setitem(self, key, value) -> None: + def __setitem__(self, key, value) -> None: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount(self) <= REF_COUNT and not com.is_local_in_caller_frame( + self + ): + warnings.warn( + _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 + ) + check_dict_or_set_indexers(key) key = com.apply_if_callable(key, self) @@ -3351,8 +3354,10 @@ def update(self, other: Series | Sequence | Mapping) -> None: 2 3 dtype: int64 """ - if not CHAINED_WARNING_DISABLED_INPLACE_METHOD: - if sys.getrefcount(self) <= REF_COUNT: + if not CHAINED_WARNING_DISABLED: + if sys.getrefcount( + self + ) <= REF_COUNT_METHOD and not com.is_local_in_caller_frame(self): warnings.warn( _chained_assignment_method_msg, ChainedAssignmentError, diff --git a/pandas/tests/copy_view/test_clip.py b/pandas/tests/copy_view/test_clip.py index dcc232f334a92..56df33db6d416 100644 --- a/pandas/tests/copy_view/test_clip.py +++ b/pandas/tests/copy_view/test_clip.py @@ -63,10 +63,10 @@ def test_clip_no_op(): def test_clip_chained_inplace(): df = DataFrame({"a": [1, 4, 2], "b": 1}) df_orig = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df["a"].clip(1, 2, inplace=True) tm.assert_frame_equal(df, df_orig) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df[["a"]].clip(1, 2, inplace=True) tm.assert_frame_equal(df, df_orig) diff --git a/pandas/tests/copy_view/test_interp_fillna.py b/pandas/tests/copy_view/test_interp_fillna.py index eb621fefc4295..9aed5d3a8d320 100644 --- a/pandas/tests/copy_view/test_interp_fillna.py +++ b/pandas/tests/copy_view/test_interp_fillna.py @@ -278,11 +278,11 @@ def test_fillna_inplace_ea_noop_shares_memory(any_numeric_ea_and_arrow_dtype): def test_fillna_chained_assignment(): df = DataFrame({"a": [1, np.nan, 2], "b": 1}) df_orig = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df["a"].fillna(100, inplace=True) tm.assert_frame_equal(df, df_orig) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df[["a"]].fillna(100, inplace=True) tm.assert_frame_equal(df, df_orig) @@ -291,10 +291,10 @@ def test_fillna_chained_assignment(): def test_interpolate_chained_assignment(func): df = DataFrame({"a": [1, np.nan, 2], "b": 1}) df_orig = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): getattr(df["a"], func)(inplace=True) tm.assert_frame_equal(df, df_orig) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): getattr(df[["a"]], func)(inplace=True) tm.assert_frame_equal(df, df_orig) diff --git a/pandas/tests/copy_view/test_methods.py b/pandas/tests/copy_view/test_methods.py index 9d3c2f3b929f5..49ce689e5f517 100644 --- a/pandas/tests/copy_view/test_methods.py +++ b/pandas/tests/copy_view/test_methods.py @@ -1205,11 +1205,11 @@ def test_where_mask_noop_on_single_column(dtype, val, func): def test_chained_where_mask(func): df = DataFrame({"a": [1, 4, 2], "b": 1}) df_orig = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): getattr(df["a"], func)(df["a"] > 2, 5, inplace=True) tm.assert_frame_equal(df, df_orig) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): getattr(df[["a"]], func)(df["a"] > 2, 5, inplace=True) tm.assert_frame_equal(df, df_orig) @@ -1391,11 +1391,11 @@ def test_update_chained_assignment(): df = DataFrame({"a": [1, 2, 3]}) ser2 = Series([100.0], index=[1]) df_orig = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df["a"].update(ser2) tm.assert_frame_equal(df, df_orig) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df[["a"]].update(ser2.to_frame()) tm.assert_frame_equal(df, df_orig) diff --git a/pandas/tests/copy_view/test_replace.py b/pandas/tests/copy_view/test_replace.py index bbdd759128e46..d4838a5e68ab8 100644 --- a/pandas/tests/copy_view/test_replace.py +++ b/pandas/tests/copy_view/test_replace.py @@ -319,11 +319,11 @@ def test_replace_columnwise_no_op(): def test_replace_chained_assignment(): df = DataFrame({"a": [1, np.nan, 2], "b": 1}) df_orig = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df["a"].replace(1, 100, inplace=True) tm.assert_frame_equal(df, df_orig) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df[["a"]].replace(1, 100, inplace=True) tm.assert_frame_equal(df, df_orig) diff --git a/pandas/tests/frame/methods/test_fillna.py b/pandas/tests/frame/methods/test_fillna.py index d229fe5aaaa84..e4e6975ecd9af 100644 --- a/pandas/tests/frame/methods/test_fillna.py +++ b/pandas/tests/frame/methods/test_fillna.py @@ -42,7 +42,7 @@ def test_fillna_on_column_view(self): arr = np.full((40, 50), np.nan) df = DataFrame(arr, copy=False) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df[0].fillna(-1, inplace=True) assert np.isnan(arr[:, 0]).all() diff --git a/pandas/tests/frame/methods/test_interpolate.py b/pandas/tests/frame/methods/test_interpolate.py index f512ed3e4a0af..25d4019fda9f8 100644 --- a/pandas/tests/frame/methods/test_interpolate.py +++ b/pandas/tests/frame/methods/test_interpolate.py @@ -310,7 +310,7 @@ def test_interp_inplace(self): expected = df.copy() result = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): return_value = result["a"].interpolate(inplace=True) assert return_value is None tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/frame/test_block_internals.py b/pandas/tests/frame/test_block_internals.py index 0caaafec909ac..444e02844a454 100644 --- a/pandas/tests/frame/test_block_internals.py +++ b/pandas/tests/frame/test_block_internals.py @@ -378,7 +378,7 @@ def test_update_inplace_sets_valid_block_values(): df = DataFrame({"a": Series([1, 2, None], dtype="category")}) # inplace update of a single column - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df["a"].fillna(1, inplace=True) # check we haven't put a Series into any block.values diff --git a/pandas/tests/indexing/multiindex/test_chaining_and_caching.py b/pandas/tests/indexing/multiindex/test_chaining_and_caching.py index 7c4fbc21e7f63..c7ed21a2cc001 100644 --- a/pandas/tests/indexing/multiindex/test_chaining_and_caching.py +++ b/pandas/tests/indexing/multiindex/test_chaining_and_caching.py @@ -28,7 +28,7 @@ def test_detect_chained_assignment(): multiind = MultiIndex.from_tuples(tuples, names=["part", "side"]) zed = DataFrame(events, index=["a", "b"], columns=multiind) - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): zed["eyes"]["right"].fillna(value=555, inplace=True) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index 0dc5b8824958e..f2a604dcc0787 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -277,7 +277,7 @@ def test_underlying_data_conversion(): df_original = df.copy() df - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df["val"].update(s) expected = df_original tm.assert_frame_equal(df, expected) diff --git a/pandas/tests/series/methods/test_update.py b/pandas/tests/series/methods/test_update.py index 9debf67af4622..9b5fb098bf3ee 100644 --- a/pandas/tests/series/methods/test_update.py +++ b/pandas/tests/series/methods/test_update.py @@ -29,7 +29,7 @@ def test_update(self): df["c"] = df["c"].astype(object) df_orig = df.copy() - with tm.raises_chained_assignment_error(inplace_method=True): + with tm.raises_chained_assignment_error(): df["c"].update(Series(["foo"], index=[0])) expected = df_orig tm.assert_frame_equal(df, expected) diff --git a/scripts/validate_unwanted_patterns.py b/scripts/validate_unwanted_patterns.py index f6ede99ecf040..435914d3f3c91 100755 --- a/scripts/validate_unwanted_patterns.py +++ b/scripts/validate_unwanted_patterns.py @@ -95,10 +95,7 @@ def _get_literal_string_prefix_len(token_string: str) -> int: return 0 -PRIVATE_FUNCTIONS_ALLOWED = { - "sys._getframe", - "sys._is_gil_enabled", -} # no known alternative +PRIVATE_FUNCTIONS_ALLOWED = {"sys._getframe"} # no known alternative def private_function_across_module(file_obj: IO[str]) -> Iterable[tuple[int, str]]: