Skip to content

Commit 203a661

Browse files
authored
CoW: Finish deprecation enforcal on block level (#57269)
* CoW: Enforce some deprecations on the block level * CoW: Finish deprecation enforcal on block level * Fixup
1 parent a90c6f8 commit 203a661

File tree

4 files changed

+46
-130
lines changed

4 files changed

+46
-130
lines changed

pandas/core/generic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6566,7 +6566,7 @@ def infer_objects(self, copy: bool | None = None) -> Self:
65666566
A int64
65676567
dtype: object
65686568
"""
6569-
new_mgr = self._mgr.convert(copy=copy)
6569+
new_mgr = self._mgr.convert()
65706570
res = self._constructor_from_mgr(new_mgr, axes=new_mgr.axes)
65716571
return res.__finalize__(self, method="infer_objects")
65726572

pandas/core/internals/blocks.py

+27-91
Original file line numberDiff line numberDiff line change
@@ -549,9 +549,7 @@ def _maybe_downcast(
549549
if caller == "fillna" and get_option("future.no_silent_downcasting"):
550550
return blocks
551551

552-
nbs = extend_blocks(
553-
[blk.convert(using_cow=True, copy=False) for blk in blocks]
554-
)
552+
nbs = extend_blocks([blk.convert() for blk in blocks])
555553
if caller == "fillna":
556554
if len(nbs) != len(blocks) or not all(
557555
x.dtype == y.dtype for x, y in zip(nbs, blocks)
@@ -575,7 +573,7 @@ def _maybe_downcast(
575573
elif caller == "where" and get_option("future.no_silent_downcasting") is True:
576574
return blocks
577575
else:
578-
nbs = extend_blocks([b._downcast_2d(downcast, True) for b in blocks])
576+
nbs = extend_blocks([b._downcast_2d(downcast) for b in blocks])
579577

580578
# When _maybe_downcast is called with caller="where", it is either
581579
# a) with downcast=False, which is a no-op (the desired future behavior)
@@ -606,7 +604,7 @@ def _maybe_downcast(
606604

607605
@final
608606
@maybe_split
609-
def _downcast_2d(self, dtype, using_cow: bool = False) -> list[Block]:
607+
def _downcast_2d(self, dtype) -> list[Block]:
610608
"""
611609
downcast specialized to 2D case post-validation.
612610
@@ -618,30 +616,19 @@ def _downcast_2d(self, dtype, using_cow: bool = False) -> list[Block]:
618616
return [self.make_block(new_values, refs=refs)]
619617

620618
@final
621-
def convert(
622-
self,
623-
*,
624-
copy: bool = True,
625-
using_cow: bool = False,
626-
) -> list[Block]:
619+
def convert(self) -> list[Block]:
627620
"""
628621
Attempt to coerce any object types to better types. Return a copy
629622
of the block (if copy = True).
630623
"""
631624
if not self.is_object:
632-
if not copy and using_cow:
633-
return [self.copy(deep=False)]
634-
return [self.copy()] if copy else [self]
625+
return [self.copy(deep=False)]
635626

636627
if self.ndim != 1 and self.shape[0] != 1:
637-
blocks = self.split_and_operate(
638-
Block.convert, copy=copy, using_cow=using_cow
639-
)
628+
blocks = self.split_and_operate(Block.convert)
640629
if all(blk.dtype.kind == "O" for blk in blocks):
641630
# Avoid fragmenting the block if convert is a no-op
642-
if using_cow:
643-
return [self.copy(deep=False)]
644-
return [self.copy()] if copy else [self]
631+
return [self.copy(deep=False)]
645632
return blocks
646633

647634
values = self.values
@@ -655,9 +642,7 @@ def convert(
655642
convert_non_numeric=True,
656643
)
657644
refs = None
658-
if copy and res_values is values:
659-
res_values = values.copy()
660-
elif res_values is values:
645+
if res_values is values:
661646
refs = self.refs
662647

663648
res_values = ensure_block_shape(res_values, self.ndim)
@@ -674,7 +659,7 @@ def convert_dtypes(
674659
dtype_backend: DtypeBackend = "numpy_nullable",
675660
) -> list[Block]:
676661
if infer_objects and self.is_object:
677-
blks = self.convert(copy=False)
662+
blks = self.convert()
678663
else:
679664
blks = [self]
680665

@@ -798,17 +783,6 @@ def _maybe_copy(self, inplace: bool) -> Self:
798783
return self.copy(deep=deep)
799784
return self.copy()
800785

801-
@final
802-
def _maybe_copy_cow_check(
803-
self, using_cow: bool = True, inplace: bool = True
804-
) -> Self:
805-
if using_cow and inplace:
806-
deep = self.refs.has_reference()
807-
blk = self.copy(deep=deep)
808-
else:
809-
blk = self if inplace else self.copy()
810-
return blk
811-
812786
@final
813787
def _get_refs_and_copy(self, inplace: bool):
814788
refs = None
@@ -820,17 +794,6 @@ def _get_refs_and_copy(self, inplace: bool):
820794
refs = self.refs
821795
return copy, refs
822796

823-
@final
824-
def _get_refs_and_copy_cow_check(self, using_cow: bool, inplace: bool):
825-
refs = None
826-
copy = not inplace
827-
if inplace:
828-
if using_cow and self.refs.has_reference():
829-
copy = True
830-
else:
831-
refs = self.refs
832-
return copy, refs
833-
834797
# ---------------------------------------------------------------------
835798
# Replace
836799

@@ -842,7 +805,6 @@ def replace(
842805
inplace: bool = False,
843806
# mask may be pre-computed if we're called from replace_list
844807
mask: npt.NDArray[np.bool_] | None = None,
845-
using_cow: bool = False,
846808
) -> list[Block]:
847809
"""
848810
replace the to_replace value with value, possible to create new
@@ -857,7 +819,7 @@ def replace(
857819
if isinstance(values, Categorical):
858820
# TODO: avoid special-casing
859821
# GH49404
860-
blk = self._maybe_copy_cow_check(using_cow, inplace)
822+
blk = self._maybe_copy(inplace)
861823
values = cast(Categorical, blk.values)
862824
values._replace(to_replace=to_replace, value=value, inplace=True)
863825
return [blk]
@@ -867,25 +829,19 @@ def replace(
867829
# replacing it is a no-op.
868830
# Note: If to_replace were a list, NDFrame.replace would call
869831
# replace_list instead of replace.
870-
if using_cow:
871-
return [self.copy(deep=False)]
872-
else:
873-
return [self] if inplace else [self.copy()]
832+
return [self.copy(deep=False)]
874833

875834
if mask is None:
876835
mask = missing.mask_missing(values, to_replace)
877836
if not mask.any():
878837
# Note: we get here with test_replace_extension_other incorrectly
879838
# bc _can_hold_element is incorrect.
880-
if using_cow:
881-
return [self.copy(deep=False)]
882-
else:
883-
return [self] if inplace else [self.copy()]
839+
return [self.copy(deep=False)]
884840

885841
elif self._can_hold_element(value):
886842
# TODO(CoW): Maybe split here as well into columns where mask has True
887843
# and rest?
888-
blk = self._maybe_copy_cow_check(using_cow, inplace)
844+
blk = self._maybe_copy(inplace)
889845
putmask_inplace(blk.values, mask, value)
890846

891847
if not (self.is_object and value is None):
@@ -894,7 +850,7 @@ def replace(
894850
if get_option("future.no_silent_downcasting") is True:
895851
blocks = [blk]
896852
else:
897-
blocks = blk.convert(copy=False, using_cow=using_cow)
853+
blocks = blk.convert()
898854
if len(blocks) > 1 or blocks[0].dtype != blk.dtype:
899855
warnings.warn(
900856
# GH#54710
@@ -935,7 +891,6 @@ def replace(
935891
value=value,
936892
inplace=True,
937893
mask=mask[i : i + 1],
938-
using_cow=using_cow,
939894
)
940895
)
941896
return blocks
@@ -947,7 +902,6 @@ def _replace_regex(
947902
value,
948903
inplace: bool = False,
949904
mask=None,
950-
using_cow: bool = False,
951905
) -> list[Block]:
952906
"""
953907
Replace elements by the given value.
@@ -962,8 +916,6 @@ def _replace_regex(
962916
Perform inplace modification.
963917
mask : array-like of bool, optional
964918
True indicate corresponding element is ignored.
965-
using_cow: bool, default False
966-
Specifying if copy on write is enabled.
967919
968920
Returns
969921
-------
@@ -972,17 +924,15 @@ def _replace_regex(
972924
if not self._can_hold_element(to_replace):
973925
# i.e. only if self.is_object is True, but could in principle include a
974926
# String ExtensionBlock
975-
if using_cow:
976-
return [self.copy(deep=False)]
977-
return [self] if inplace else [self.copy()]
927+
return [self.copy(deep=False)]
978928

979929
rx = re.compile(to_replace)
980930

981-
block = self._maybe_copy_cow_check(using_cow, inplace)
931+
block = self._maybe_copy(inplace)
982932

983933
replace_regex(block.values, rx, value, mask)
984934

985-
nbs = block.convert(copy=False, using_cow=using_cow)
935+
nbs = block.convert()
986936
opt = get_option("future.no_silent_downcasting")
987937
if (len(nbs) > 1 or nbs[0].dtype != block.dtype) and not opt:
988938
warnings.warn(
@@ -1005,7 +955,6 @@ def replace_list(
1005955
dest_list: Sequence[Any],
1006956
inplace: bool = False,
1007957
regex: bool = False,
1008-
using_cow: bool = False,
1009958
) -> list[Block]:
1010959
"""
1011960
See BlockManager.replace_list docstring.
@@ -1015,7 +964,7 @@ def replace_list(
1015964
if isinstance(values, Categorical):
1016965
# TODO: avoid special-casing
1017966
# GH49404
1018-
blk = self._maybe_copy_cow_check(using_cow, inplace)
967+
blk = self._maybe_copy(inplace)
1019968
values = cast(Categorical, blk.values)
1020969
values._replace(to_replace=src_list, value=dest_list, inplace=True)
1021970
return [blk]
@@ -1025,10 +974,7 @@ def replace_list(
1025974
(x, y) for x, y in zip(src_list, dest_list) if self._can_hold_element(x)
1026975
]
1027976
if not len(pairs):
1028-
if using_cow:
1029-
return [self.copy(deep=False)]
1030-
# shortcut, nothing to replace
1031-
return [self] if inplace else [self.copy()]
977+
return [self.copy(deep=False)]
1032978

1033979
src_len = len(pairs) - 1
1034980

@@ -1055,12 +1001,9 @@ def replace_list(
10551001
if inplace:
10561002
masks = list(masks)
10571003

1058-
if using_cow:
1059-
# Don't set up refs here, otherwise we will think that we have
1060-
# references when we check again later
1061-
rb = [self]
1062-
else:
1063-
rb = [self if inplace else self.copy()]
1004+
# Don't set up refs here, otherwise we will think that we have
1005+
# references when we check again later
1006+
rb = [self]
10641007

10651008
opt = get_option("future.no_silent_downcasting")
10661009
for i, ((src, dest), mask) in enumerate(zip(pairs, masks)):
@@ -1087,10 +1030,9 @@ def replace_list(
10871030
mask=m,
10881031
inplace=inplace,
10891032
regex=regex,
1090-
using_cow=using_cow,
10911033
)
10921034

1093-
if using_cow and i != src_len:
1035+
if i != src_len:
10941036
# This is ugly, but we have to get rid of intermediate refs
10951037
# that did not go out of scope yet, otherwise we will trigger
10961038
# many unnecessary copies
@@ -1109,9 +1051,7 @@ def replace_list(
11091051
# GH#44498 avoid unwanted cast-back
11101052
nbs = []
11111053
for res_blk in result:
1112-
converted = res_blk.convert(
1113-
copy=True and not using_cow, using_cow=using_cow
1114-
)
1054+
converted = res_blk.convert()
11151055
if len(converted) > 1 or converted[0].dtype != res_blk.dtype:
11161056
warnings.warn(
11171057
# GH#54710
@@ -1139,7 +1079,6 @@ def _replace_coerce(
11391079
mask: npt.NDArray[np.bool_],
11401080
inplace: bool = True,
11411081
regex: bool = False,
1142-
using_cow: bool = False,
11431082
) -> list[Block]:
11441083
"""
11451084
Replace value corresponding to the given boolean array with another
@@ -1175,22 +1114,19 @@ def _replace_coerce(
11751114
if mask.any():
11761115
has_ref = self.refs.has_reference()
11771116
nb = self.astype(np.dtype(object))
1178-
if (nb is self or using_cow) and not inplace:
1117+
if not inplace:
11791118
nb = nb.copy()
1180-
elif inplace and has_ref and nb.refs.has_reference() and using_cow:
1119+
elif inplace and has_ref and nb.refs.has_reference():
11811120
# no copy in astype and we had refs before
11821121
nb = nb.copy()
11831122
putmask_inplace(nb.values, mask, value)
11841123
return [nb]
1185-
if using_cow:
1186-
return [self]
1187-
return [self] if inplace else [self.copy()]
1124+
return [self]
11881125
return self.replace(
11891126
to_replace=to_replace,
11901127
value=value,
11911128
inplace=inplace,
11921129
mask=mask,
1193-
using_cow=using_cow,
11941130
)
11951131

11961132
# ---------------------------------------------------------------------

0 commit comments

Comments
 (0)