From 815b441202e277c30ccc5e3b4f271dc280c8277e Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Sat, 3 Feb 2024 23:36:08 +0000 Subject: [PATCH] Remove Copy-on-Write warning mode --- pandas/_config/__init__.py | 5 -- pandas/core/frame.py | 15 +--- pandas/core/generic.py | 112 +------------------------ pandas/core/groupby/grouper.py | 7 +- pandas/core/indexing.py | 21 +---- pandas/core/internals/blocks.py | 130 +----------------------------- pandas/core/internals/managers.py | 79 ++---------------- pandas/core/series.py | 74 ++++------------- pandas/errors/cow.py | 20 ----- 9 files changed, 35 insertions(+), 428 deletions(-) diff --git a/pandas/_config/__init__.py b/pandas/_config/__init__.py index 0594d1c190a72..9784303fc0b87 100644 --- a/pandas/_config/__init__.py +++ b/pandas/_config/__init__.py @@ -15,7 +15,6 @@ "option_context", "options", "using_copy_on_write", - "warn_copy_on_write", ] from pandas._config import config from pandas._config import dates # pyright: ignore[reportUnusedImport] # noqa: F401 @@ -35,10 +34,6 @@ def using_copy_on_write() -> bool: return True -def warn_copy_on_write() -> bool: - return False - - def using_nullable_dtypes() -> bool: _mode_options = _global_config["mode"] return _mode_options["nullable_dtypes"] diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 207e3e7635cac..b8b5df6e5145b 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -42,7 +42,6 @@ from pandas._config import ( get_option, using_copy_on_write, - warn_copy_on_write, ) from pandas._libs import ( @@ -64,7 +63,6 @@ _chained_assignment_method_msg, _chained_assignment_msg, _chained_assignment_warning_method_msg, - _chained_assignment_warning_msg, ) from pandas.util._decorators import ( Appender, @@ -4199,17 +4197,6 @@ def __setitem__(self, key, value) -> None: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) - elif not PYPY and not using_copy_on_write(): - if sys.getrefcount(self) <= 3 and ( - warn_copy_on_write() - or ( - not warn_copy_on_write() - and any(b.refs.has_reference() for b in self._mgr.blocks) - ) - ): - warnings.warn( - _chained_assignment_warning_msg, FutureWarning, stacklevel=2 - ) key = com.apply_if_callable(key, self) @@ -4550,7 +4537,7 @@ def _clear_item_cache(self) -> None: def _get_item_cache(self, item: Hashable) -> Series: """Return the cached item, item represents a label indexer.""" - if using_copy_on_write() or warn_copy_on_write(): + if using_copy_on_write(): loc = self.columns.get_loc(item) return self._ixs(loc, axis=1) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 78fbb66635dd1..5d01fb4c7e328 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -30,7 +30,6 @@ from pandas._config import ( config, using_copy_on_write, - warn_copy_on_write, ) from pandas._libs import lib @@ -105,7 +104,6 @@ from pandas.errors.cow import ( _chained_assignment_method_msg, _chained_assignment_warning_method_msg, - _check_cacher, ) from pandas.util._decorators import ( deprecate_nonkeyword_arguments, @@ -4401,7 +4399,7 @@ def _check_setitem_copy(self, t: str = "setting", force: bool_t = False) -> None df.iloc[0:5]['group'] = 'a' """ - if using_copy_on_write() or warn_copy_on_write(): + if using_copy_on_write(): return # return early if the check is not needed @@ -7250,22 +7248,6 @@ def fillna( ChainedAssignmentError, stacklevel=2, ) - elif ( - not PYPY - and not using_copy_on_write() - and self._is_view_after_cow_rules() - ): - ctr = sys.getrefcount(self) - ref_count = REF_COUNT - if isinstance(self, ABCSeries) and _check_cacher(self): - # see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221 - ref_count += 1 - if ctr <= ref_count: - warnings.warn( - _chained_assignment_warning_method_msg, - FutureWarning, - stacklevel=2, - ) value, method = validate_fillna_kwargs(value, method) if method is not None: @@ -7553,22 +7535,6 @@ def ffill( ChainedAssignmentError, stacklevel=2, ) - elif ( - not PYPY - and not using_copy_on_write() - and self._is_view_after_cow_rules() - ): - ctr = sys.getrefcount(self) - ref_count = REF_COUNT - if isinstance(self, ABCSeries) and _check_cacher(self): - # see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221 - ref_count += 1 - if ctr <= ref_count: - warnings.warn( - _chained_assignment_warning_method_msg, - FutureWarning, - stacklevel=2, - ) return self._pad_or_backfill( "ffill", @@ -7757,22 +7723,6 @@ def bfill( ChainedAssignmentError, stacklevel=2, ) - elif ( - not PYPY - and not using_copy_on_write() - and self._is_view_after_cow_rules() - ): - ctr = sys.getrefcount(self) - ref_count = REF_COUNT - if isinstance(self, ABCSeries) and _check_cacher(self): - # see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221 - ref_count += 1 - if ctr <= ref_count: - warnings.warn( - _chained_assignment_warning_method_msg, - FutureWarning, - stacklevel=2, - ) return self._pad_or_backfill( "bfill", @@ -7928,26 +7878,6 @@ def replace( ChainedAssignmentError, stacklevel=2, ) - elif ( - not PYPY - and not using_copy_on_write() - and self._is_view_after_cow_rules() - ): - ctr = sys.getrefcount(self) - ref_count = REF_COUNT - if isinstance(self, ABCSeries) and _check_cacher(self): - # in non-CoW mode, chained Series access will populate the - # `_item_cache` which results in an increased ref count not below - # the threshold, while we still need to warn. We detect this case - # of a Series derived from a DataFrame through the presence of - # checking the `_cacher` - ref_count += 1 - if ctr <= ref_count: - warnings.warn( - _chained_assignment_warning_method_msg, - FutureWarning, - stacklevel=2, - ) if not is_bool(regex) and to_replace is not None: raise ValueError("'to_replace' must be 'None' if 'regex' is not a bool") @@ -8378,22 +8308,6 @@ def interpolate( ChainedAssignmentError, stacklevel=2, ) - elif ( - not PYPY - and not using_copy_on_write() - and self._is_view_after_cow_rules() - ): - ctr = sys.getrefcount(self) - ref_count = REF_COUNT - if isinstance(self, ABCSeries) and _check_cacher(self): - # see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221 - ref_count += 1 - if ctr <= ref_count: - warnings.warn( - _chained_assignment_warning_method_msg, - FutureWarning, - stacklevel=2, - ) axis = self._get_axis_number(axis) @@ -10563,7 +10477,6 @@ def _where( inplace: bool_t = False, axis: Axis | None = None, level=None, - warn: bool_t = True, ): """ Equivalent to public method `where`, except that `other` is not @@ -10694,7 +10607,7 @@ def _where( # we may have different type blocks come out of putmask, so # reconstruct the block manager - new_data = self._mgr.putmask(mask=cond, new=other, align=align, warn=warn) + new_data = self._mgr.putmask(mask=cond, new=other, align=align) result = self._constructor_from_mgr(new_data, axes=new_data.axes) return self._update_inplace(result) @@ -12560,29 +12473,8 @@ def _inplace_method(self, other, op) -> Self: """ Wrap arithmetic method to operate inplace. """ - warn = True - if not PYPY and warn_copy_on_write(): - if sys.getrefcount(self) <= REF_COUNT + 2: - # we are probably in an inplace setitem context (e.g. df['a'] += 1) - warn = False - result = op(self, other) - if ( - self.ndim == 1 - and result._indexed_same(self) - and result.dtype == self.dtype - and not using_copy_on_write() - and not (warn_copy_on_write() and not warn) - ): - # GH#36498 this inplace op can _actually_ be inplace. - # Item "BlockManager" of "Union[BlockManager, SingleBlockManager]" has - # no attribute "setitem_inplace" - self._mgr.setitem_inplace( # type: ignore[union-attr] - slice(None), result._values, warn=warn - ) - return self - # Delete cacher self._reset_cacher() diff --git a/pandas/core/groupby/grouper.py b/pandas/core/groupby/grouper.py index f377c9d03d05a..1e6658e5dfd39 100644 --- a/pandas/core/groupby/grouper.py +++ b/pandas/core/groupby/grouper.py @@ -12,10 +12,7 @@ import numpy as np -from pandas._config import ( - using_copy_on_write, - warn_copy_on_write, -) +from pandas._config import using_copy_on_write from pandas._libs.tslibs import OutOfBoundsDatetime from pandas.errors import InvalidIndexError @@ -962,7 +959,7 @@ def is_in_axis(key) -> bool: def is_in_obj(gpr) -> bool: if not hasattr(gpr, "name"): return False - if using_copy_on_write() or warn_copy_on_write(): + if using_copy_on_write(): # For the CoW case, we check the references to determine if the # series is part of the object try: diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 24f3ff4279a84..b58c3179dec09 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -13,10 +13,7 @@ import numpy as np -from pandas._config import ( - using_copy_on_write, - warn_copy_on_write, -) +from pandas._config import using_copy_on_write from pandas._libs.indexing import NDFrameIndexerBase from pandas._libs.lib import item_from_zerodim @@ -28,11 +25,7 @@ InvalidIndexError, LossySetitemError, ) -from pandas.errors.cow import ( - _chained_assignment_msg, - _chained_assignment_warning_msg, - _check_cacher, -) +from pandas.errors.cow import _chained_assignment_msg from pandas.util._decorators import doc from pandas.util._exceptions import find_stack_level @@ -889,16 +882,6 @@ def __setitem__(self, key, value) -> None: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) - elif not PYPY and not using_copy_on_write(): - ctr = sys.getrefcount(self.obj) - ref_count = 2 - if not warn_copy_on_write() and _check_cacher(self.obj): - # see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221 - ref_count += 1 - if ctr <= ref_count: - warnings.warn( - _chained_assignment_warning_msg, FutureWarning, stacklevel=2 - ) check_dict_or_set_indexers(key) if isinstance(key, tuple): diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 1237c5b86d298..bb65e7a4d0838 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -19,7 +19,6 @@ from pandas._config import ( get_option, using_copy_on_write, - warn_copy_on_write, ) from pandas._libs import ( @@ -834,7 +833,6 @@ def replace( # mask may be pre-computed if we're called from replace_list mask: npt.NDArray[np.bool_] | None = None, using_cow: bool = False, - already_warned=None, ) -> list[Block]: """ replace the to_replace value with value, possible to create new @@ -879,19 +877,6 @@ def replace( # and rest? blk = self._maybe_copy(using_cow, inplace) putmask_inplace(blk.values, mask, value) - if ( - inplace - and warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True if not (self.is_object and value is None): # if the user *explicitly* gave None, we keep None, otherwise @@ -953,7 +938,6 @@ def _replace_regex( inplace: bool = False, mask=None, using_cow: bool = False, - already_warned=None, ) -> list[Block]: """ Replace elements by the given value. @@ -988,20 +972,6 @@ def _replace_regex( replace_regex(block.values, rx, value, mask) - if ( - inplace - and warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True - nbs = block.convert(copy=False, using_cow=using_cow) opt = get_option("future.no_silent_downcasting") if (len(nbs) > 1 or nbs[0].dtype != block.dtype) and not opt: @@ -1026,7 +996,6 @@ def replace_list( inplace: bool = False, regex: bool = False, using_cow: bool = False, - already_warned=None, ) -> list[Block]: """ See BlockManager.replace_list docstring. @@ -1083,20 +1052,6 @@ def replace_list( else: rb = [self if inplace else self.copy()] - if ( - inplace - and warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True - opt = get_option("future.no_silent_downcasting") for i, ((src, dest), mask) in enumerate(zip(pairs, masks)): convert = i == src_len # only convert once at the end @@ -1428,9 +1383,7 @@ def setitem(self, indexer, value, using_cow: bool = False) -> Block: values[indexer] = casted return self - def putmask( - self, mask, new, using_cow: bool = False, already_warned=None - ) -> list[Block]: + def putmask(self, mask, new, using_cow: bool = False) -> list[Block]: """ putmask the data to the block; it is possible that we may create a new dtype of block @@ -1463,19 +1416,6 @@ def putmask( return [self.copy(deep=False)] return [self] - if ( - warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True - try: casted = np_can_hold_element(values.dtype, new) @@ -1640,7 +1580,6 @@ def fillna( inplace: bool = False, downcast=None, using_cow: bool = False, - already_warned=None, ) -> list[Block]: """ fillna on the block with the value. If we fail, then convert to @@ -1676,9 +1615,7 @@ def fillna( mask[mask.cumsum(self.ndim - 1) > limit] = False if inplace: - nbs = self.putmask( - mask.T, value, using_cow=using_cow, already_warned=already_warned - ) + nbs = self.putmask(mask.T, value, using_cow=using_cow) else: # without _downcast, we would break # test_fillna_dtype_conversion_equiv_replace @@ -1706,7 +1643,6 @@ def pad_or_backfill( limit_area: Literal["inside", "outside"] | None = None, downcast: Literal["infer"] | None = None, using_cow: bool = False, - already_warned=None, ) -> list[Block]: if not self._can_hold_na: # If there are no NAs, then interpolate is a no-op @@ -1727,19 +1663,6 @@ def pad_or_backfill( limit_area=limit_area, copy=copy, ) - if ( - not copy - and warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True if axis == 1: new_values = new_values.T @@ -1760,7 +1683,6 @@ def interpolate( limit_area: Literal["inside", "outside"] | None = None, downcast: Literal["infer"] | None = None, using_cow: bool = False, - already_warned=None, **kwargs, ) -> list[Block]: inplace = validate_bool_kwarg(inplace, "inplace") @@ -1799,20 +1721,6 @@ def interpolate( ) data = extract_array(new_values, extract_numpy=True) - if ( - not copy - and warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True - nb = self.make_block_same_class(data, refs=refs) return nb._maybe_downcast([nb], downcast, using_cow, caller="interpolate") @@ -2141,9 +2049,7 @@ def where( return [nb] @final - def putmask( - self, mask, new, using_cow: bool = False, already_warned=None - ) -> list[Block]: + def putmask(self, mask, new, using_cow: bool = False) -> list[Block]: """ See Block.putmask.__doc__ """ @@ -2161,19 +2067,6 @@ def putmask( return [self.copy(deep=False)] return [self] - if ( - warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True - self = self._maybe_copy(using_cow, inplace=True) values = self.values if values.ndim == 2: @@ -2257,7 +2150,6 @@ def pad_or_backfill( limit_area: Literal["inside", "outside"] | None = None, downcast: Literal["infer"] | None = None, using_cow: bool = False, - already_warned=None, ) -> list[Block]: values = self.values @@ -2300,7 +2192,6 @@ def fillna( inplace: bool = False, downcast=None, using_cow: bool = False, - already_warned=None, ) -> list[Block]: if isinstance(self.dtype, IntervalDtype): # Block.fillna handles coercion (test_fillna_interval) @@ -2310,7 +2201,6 @@ def fillna( inplace=inplace, downcast=downcast, using_cow=using_cow, - already_warned=already_warned, ) if using_cow and self._can_hold_na and not self.values._hasna: refs = self.refs @@ -2338,20 +2228,6 @@ def fillna( DeprecationWarning, stacklevel=find_stack_level(), ) - else: - if ( - not copy - and warn_copy_on_write() - and already_warned is not None - and not already_warned.warned_already - ): - if self.refs.has_reference(): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - already_warned.warned_already = True nb = self.make_block_same_class(new_values, refs=refs) return nb._maybe_downcast([nb], downcast, using_cow=using_cow, caller="fillna") diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index e1a18cb79a1d6..fa54fde2ece84 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -18,10 +18,7 @@ import numpy as np -from pandas._config import ( - using_copy_on_write, - warn_copy_on_write, -) +from pandas._config import using_copy_on_write from pandas._libs import ( algos as libalgos, @@ -66,11 +63,7 @@ ) import pandas.core.algorithms as algos -from pandas.core.arrays import ( - ArrowExtensionArray, - ArrowStringArray, - DatetimeArray, -) +from pandas.core.arrays import DatetimeArray from pandas.core.arrays._mixins import NDArrayBackedExtensionArray from pandas.core.base import PandasObject from pandas.core.construction import ( @@ -84,8 +77,6 @@ ensure_index, ) from pandas.core.internals.blocks import ( - COW_WARNING_GENERAL_MSG, - COW_WARNING_SETITEM_MSG, Block, NumpyBlock, ensure_block_shape, @@ -146,16 +137,6 @@ def ensure_np_dtype(dtype: DtypeObj) -> np.dtype: return dtype -class _AlreadyWarned: - def __init__(self) -> None: - # This class is used on the manager level to the block level to - # ensure that we warn only once. The block method can update the - # warned_already option without returning a value to keep the - # interface consistent. This is only a temporary solution for - # CoW warnings. - self.warned_already = False - - class BaseBlockManager(PandasObject): """ Core internal data structure to implement DataFrame, Series, etc. @@ -490,7 +471,6 @@ def fillna(self, value, limit: int | None, inplace: bool, downcast) -> Self: inplace=inplace, downcast=downcast, using_cow=using_copy_on_write(), - already_warned=_AlreadyWarned(), ) @final @@ -510,26 +490,19 @@ def where(self, other, cond, align: bool) -> Self: ) @final - def putmask(self, mask, new, align: bool = True, warn: bool = True) -> Self: + def putmask(self, mask, new, align: bool = True) -> Self: if align: align_keys = ["new", "mask"] else: align_keys = ["mask"] new = extract_array(new, extract_numpy=True) - already_warned = None - if warn_copy_on_write(): - already_warned = _AlreadyWarned() - if not warn: - already_warned.warned_already = True - return self.apply( "putmask", align_keys=align_keys, mask=mask, new=new, using_cow=using_copy_on_write(), - already_warned=already_warned, ) @final @@ -552,7 +525,6 @@ def replace(self, to_replace, value, inplace: bool) -> Self: value=value, inplace=inplace, using_cow=using_copy_on_write(), - already_warned=_AlreadyWarned(), ) @final @@ -561,7 +533,6 @@ def replace_regex(self, **kwargs) -> Self: "_replace_regex", **kwargs, using_cow=using_copy_on_write(), - already_warned=_AlreadyWarned(), ) @final @@ -582,7 +553,6 @@ def replace_list( inplace=inplace, regex=regex, using_cow=using_copy_on_write(), - already_warned=_AlreadyWarned(), ) bm._consolidate_inplace() return bm @@ -593,7 +563,6 @@ def interpolate(self, inplace: bool, **kwargs) -> Self: inplace=inplace, **kwargs, using_cow=using_copy_on_write(), - already_warned=_AlreadyWarned(), ) def pad_or_backfill(self, inplace: bool, **kwargs) -> Self: @@ -602,7 +571,6 @@ def pad_or_backfill(self, inplace: bool, **kwargs) -> Self: inplace=inplace, **kwargs, using_cow=using_copy_on_write(), - already_warned=_AlreadyWarned(), ) def shift(self, periods: int, fill_value) -> Self: @@ -611,7 +579,7 @@ def shift(self, periods: int, fill_value) -> Self: return self.apply("shift", periods=periods, fill_value=fill_value) - def setitem(self, indexer, value, warn: bool = True) -> Self: + def setitem(self, indexer, value) -> Self: """ Set values with indexer. @@ -620,14 +588,7 @@ def setitem(self, indexer, value, warn: bool = True) -> Self: if isinstance(indexer, np.ndarray) and indexer.ndim > self.ndim: raise ValueError(f"Cannot set values with ndim > {self.ndim}") - if warn and warn_copy_on_write() and not self._has_no_reference(0): - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - - elif using_copy_on_write() and not self._has_no_reference(0): + if using_copy_on_write() and not self._has_no_reference(0): # this method is only called if there is a single block -> hardcoded 0 # Split blocks to only copy the columns we want to modify if self.ndim == 2 and isinstance(indexer, tuple): @@ -1576,17 +1537,7 @@ def column_setitem( This is a method on the BlockManager level, to avoid creating an intermediate Series at the DataFrame level (`s = df[loc]; s[idx] = value`) """ - needs_to_warn = False - if warn_copy_on_write() and not self._has_no_reference(loc): - if not isinstance( - self.blocks[self.blknos[loc]].values, - (ArrowExtensionArray, ArrowStringArray), - ): - # We might raise if we are in an expansion case, so defer - # warning till we actually updated - needs_to_warn = True - - elif using_copy_on_write() and not self._has_no_reference(loc): + if using_copy_on_write() and not self._has_no_reference(loc): blkno = self.blknos[loc] # Split blocks to only copy the column we want to modify blk_loc = self.blklocs[loc] @@ -1609,13 +1560,6 @@ def column_setitem( new_mgr = col_mgr.setitem((idx,), value) self.iset(loc, new_mgr._block.values, inplace=True) - if needs_to_warn: - warnings.warn( - COW_WARNING_GENERAL_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) - def insert(self, loc: int, item: Hashable, value: ArrayLike, refs=None) -> None: """ Insert item at selected position. @@ -2298,7 +2242,7 @@ def get_numeric_data(self) -> Self: def _can_hold_na(self) -> bool: return self._block._can_hold_na - def setitem_inplace(self, indexer, value, warn: bool = True) -> None: + def setitem_inplace(self, indexer, value) -> None: """ Set values with indexer. @@ -2309,17 +2253,10 @@ def setitem_inplace(self, indexer, value, warn: bool = True) -> None: the dtype. """ using_cow = using_copy_on_write() - warn_cow = warn_copy_on_write() - if (using_cow or warn_cow) and not self._has_no_reference(0): + if using_cow and not self._has_no_reference(0): if using_cow: self.blocks = (self._block.copy(),) self._cache.clear() - elif warn_cow and warn: - warnings.warn( - COW_WARNING_SETITEM_MSG, - FutureWarning, - stacklevel=find_stack_level(), - ) arr = self.array diff --git a/pandas/core/series.py b/pandas/core/series.py index 657b384c57235..94be7bdbaca16 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -26,10 +26,7 @@ import numpy as np -from pandas._config import ( - using_copy_on_write, - warn_copy_on_write, -) +from pandas._config import using_copy_on_write from pandas._libs import ( lib, @@ -48,9 +45,6 @@ from pandas.errors.cow import ( _chained_assignment_method_msg, _chained_assignment_msg, - _chained_assignment_warning_method_msg, - _chained_assignment_warning_msg, - _check_cacher, ) from pandas.util._decorators import ( Appender, @@ -1075,7 +1069,7 @@ def __getitem__(self, key): key = com.apply_if_callable(key, self) if key is Ellipsis: - if using_copy_on_write() or warn_copy_on_write(): + if using_copy_on_write(): return self.copy(deep=False) return self @@ -1237,29 +1231,11 @@ def _get_value(self, label, takeable: bool = False): return self.iloc[loc] def __setitem__(self, key, value) -> None: - warn = True if not PYPY and using_copy_on_write(): if sys.getrefcount(self) <= 3: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) - elif not PYPY and not using_copy_on_write(): - ctr = sys.getrefcount(self) - ref_count = 3 - if not warn_copy_on_write() and _check_cacher(self): - # see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221 - ref_count += 1 - if ctr <= ref_count and ( - warn_copy_on_write() - or ( - not warn_copy_on_write() - and self._mgr.blocks[0].refs.has_reference() - ) - ): - warn = False - warnings.warn( - _chained_assignment_warning_msg, FutureWarning, stacklevel=2 - ) check_dict_or_set_indexers(key) key = com.apply_if_callable(key, self) @@ -1270,10 +1246,10 @@ def __setitem__(self, key, value) -> None: if isinstance(key, slice): indexer = self.index._convert_slice_indexer(key, kind="getitem") - return self._set_values(indexer, value, warn=warn) + return self._set_values(indexer, value) try: - self._set_with_engine(key, value, warn=warn) + self._set_with_engine(key, value) except KeyError: # We have a scalar (or for MultiIndex or object-dtype, scalar-like) # key that is not present in self.index. @@ -1332,25 +1308,25 @@ def __setitem__(self, key, value) -> None: # otherwise with listlike other we interpret series[mask] = other # as series[mask] = other[mask] try: - self._where(~key, value, inplace=True, warn=warn) + self._where(~key, value, inplace=True) except InvalidIndexError: # test_where_dups self.iloc[key] = value return else: - self._set_with(key, value, warn=warn) + self._set_with(key, value) if cacher_needs_updating: self._maybe_update_cacher(inplace=True) - def _set_with_engine(self, key, value, warn: bool = True) -> None: + def _set_with_engine(self, key, value) -> None: loc = self.index.get_loc(key) # this is equivalent to self._values[key] = value - self._mgr.setitem_inplace(loc, value, warn=warn) + self._mgr.setitem_inplace(loc, value) - def _set_with(self, key, value, warn: bool = True) -> None: + def _set_with(self, key, value) -> None: # We got here via exception-handling off of InvalidIndexError, so # key should always be listlike at this point. assert not isinstance(key, tuple) @@ -1361,7 +1337,7 @@ def _set_with(self, key, value, warn: bool = True) -> None: if not self.index._should_fallback_to_positional: # Regardless of the key type, we're treating it as labels - self._set_labels(key, value, warn=warn) + self._set_labels(key, value) else: # Note: key_type == "boolean" should not occur because that @@ -1378,23 +1354,23 @@ def _set_with(self, key, value, warn: bool = True) -> None: FutureWarning, stacklevel=find_stack_level(), ) - self._set_values(key, value, warn=warn) + self._set_values(key, value) else: - self._set_labels(key, value, warn=warn) + self._set_labels(key, value) - def _set_labels(self, key, value, warn: bool = True) -> None: + def _set_labels(self, key, value) -> None: key = com.asarray_tuplesafe(key) indexer: np.ndarray = self.index.get_indexer(key) mask = indexer == -1 if mask.any(): raise KeyError(f"{key[mask]} not in index") - self._set_values(indexer, value, warn=warn) + self._set_values(indexer, value) - def _set_values(self, key, value, warn: bool = True) -> None: + def _set_values(self, key, value) -> None: if isinstance(key, (Index, Series)): key = key._values - self._mgr = self._mgr.setitem(indexer=key, value=value, warn=warn) + self._mgr = self._mgr.setitem(indexer=key, value=value) self._maybe_update_cacher() def _set_value(self, label, value, takeable: bool = False) -> None: @@ -3594,18 +3570,6 @@ def update(self, other: Series | Sequence | Mapping) -> None: ChainedAssignmentError, stacklevel=2, ) - elif not PYPY and not using_copy_on_write() and self._is_view_after_cow_rules(): - ctr = sys.getrefcount(self) - ref_count = REF_COUNT - if _check_cacher(self): - # see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221 - ref_count += 1 - if ctr <= ref_count: - warnings.warn( - _chained_assignment_warning_method_msg, - FutureWarning, - stacklevel=2, - ) if not isinstance(other, Series): other = Series(other) @@ -4755,11 +4719,7 @@ def transform( ) -> DataFrame | Series: # Validate axis argument self._get_axis_number(axis) - ser = ( - self.copy(deep=False) - if using_copy_on_write() or warn_copy_on_write() - else self - ) + ser = self.copy(deep=False) if using_copy_on_write() else self result = SeriesApply(ser, func=func, args=args, kwargs=kwargs).transform() return result diff --git a/pandas/errors/cow.py b/pandas/errors/cow.py index 2215ec2148757..9a3f6f4cc8efc 100644 --- a/pandas/errors/cow.py +++ b/pandas/errors/cow.py @@ -1,5 +1,3 @@ -from typing import Any - _chained_assignment_msg = ( "A value is trying to be set on a copy of a DataFrame or Series " "through chained assignment.\n" @@ -54,21 +52,3 @@ "df[col] = df[col].method(value) instead, to perform " "the operation inplace on the original object.\n\n" ) - - -def _check_cacher(obj: Any) -> bool: - # This is a mess, selection paths that return a view set the _cacher attribute - # on the Series; most of them also set _item_cache which adds 1 to our relevant - # reference count, but iloc does not, so we have to check if we are actually - # in the item cache - if hasattr(obj, "_cacher"): - parent = obj._cacher[1]() - # parent could be dead - if parent is None: - return False - if hasattr(parent, "_item_cache"): - if obj._cacher[0] in parent._item_cache: - # Check if we are actually the item from item_cache, iloc creates a - # new object - return obj is parent._item_cache[obj._cacher[0]] - return False