From 36fd33741f6280e9cf60026e5bd5a2d185c893f6 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Fri, 25 Jul 2025 09:36:54 -0600 Subject: [PATCH 1/4] MAINT: disable the warning on 3.14 and newer --- pandas/_testing/contexts.py | 7 +++++-- pandas/compat/__init__.py | 2 ++ pandas/compat/_constants.py | 2 ++ pandas/core/frame.py | 11 ++++++---- pandas/core/generic.py | 21 +++++++++++-------- pandas/core/indexing.py | 8 +++++-- pandas/core/series.py | 11 ++++++---- .../test_chained_assignment_deprecation.py | 3 +++ 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/pandas/_testing/contexts.py b/pandas/_testing/contexts.py index da147c117ad43..b79cd797a3707 100644 --- a/pandas/_testing/contexts.py +++ b/pandas/_testing/contexts.py @@ -12,7 +12,10 @@ ) import uuid -from pandas.compat import PYPY +from pandas.compat import ( + PYPY, + WARNING_CHECK_BROKEN, +) from pandas.errors import ChainedAssignmentError from pandas.io.common import get_handle @@ -163,7 +166,7 @@ def with_csv_dialect(name: str, **kwargs) -> Generator[None]: def raises_chained_assignment_error(extra_warnings=(), extra_match=()): from pandas._testing import assert_produces_warning - if PYPY: + if PYPY or WARNING_CHECK_BROKEN: if not extra_warnings: from contextlib import nullcontext diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index f636bed5e4e66..bc1b0757438a4 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -21,6 +21,7 @@ PY312, PY314, PYPY, + WARNING_CHECK_BROKEN, WASM, ) from pandas.compat.numpy import is_numpy_dev @@ -158,6 +159,7 @@ def is_ci_environment() -> bool: "PY314", "PYARROW_MIN_VERSION", "PYPY", + "WARNING_CHECK_BROKEN", "WASM", "is_numpy_dev", "pa_version_under14p0", diff --git a/pandas/compat/_constants.py b/pandas/compat/_constants.py index c14e20fb7042c..722c0ea785477 100644 --- a/pandas/compat/_constants.py +++ b/pandas/compat/_constants.py @@ -19,6 +19,8 @@ WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"]) ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "") REF_COUNT = 2 +WARNING_CHECK_BROKEN = PY314 + __all__ = [ "IS64", diff --git a/pandas/core/frame.py b/pandas/core/frame.py index c1f8be1381b23..0911a2d25af46 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -50,7 +50,10 @@ from pandas._libs.hashtable import duplicated from pandas._libs.lib import is_range_indexer from pandas.compat import PYPY -from pandas.compat._constants import REF_COUNT +from pandas.compat._constants import ( + REF_COUNT, + WARNING_CHECK_BROKEN, +) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv from pandas.errors import ( @@ -4296,8 +4299,8 @@ def __setitem__(self, key, value) -> None: z 3 50 # Values for 'a' and 'b' are completely ignored! """ - if not PYPY: - if sys.getrefcount(self) <= 3: + if not PYPY and not WARNING_CHECK_BROKEN: + if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) @@ -9211,7 +9214,7 @@ def update( 1 2 500.0 2 3 6.0 """ - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 93a7de467dd97..bec6c5e1b9cbe 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -83,7 +83,10 @@ npt, ) from pandas.compat import PYPY -from pandas.compat._constants import REF_COUNT +from pandas.compat._constants import ( + REF_COUNT, + WARNING_CHECK_BROKEN, +) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv from pandas.errors import ( @@ -7070,7 +7073,7 @@ def fillna( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7301,7 +7304,7 @@ def ffill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7441,7 +7444,7 @@ def bfill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7526,7 +7529,7 @@ def replace( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7889,7 +7892,7 @@ def interpolate( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -8473,7 +8476,7 @@ def clip( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10083,7 +10086,7 @@ def where( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10147,7 +10150,7 @@ def mask( ) -> Self | None: inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 776356abc71d6..c43a79316e1f3 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -16,6 +16,10 @@ from pandas._libs.indexing import NDFrameIndexerBase from pandas._libs.lib import item_from_zerodim from pandas.compat import PYPY +from pandas.compat._constants import ( + REF_COUNT, + WARNING_CHECK_BROKEN, +) from pandas.errors import ( AbstractMethodError, ChainedAssignmentError, @@ -913,8 +917,8 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None) -> None: @final def __setitem__(self, key, value) -> None: - if not PYPY: - if sys.getrefcount(self.obj) <= 2: + if not PYPY and not WARNING_CHECK_BROKEN: + if sys.getrefcount(self.obj) <= REF_COUNT: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) diff --git a/pandas/core/series.py b/pandas/core/series.py index 56ef313d1a73a..ef4996f0ae5e4 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -35,7 +35,10 @@ ) from pandas._libs.lib import is_range_indexer from pandas.compat import PYPY -from pandas.compat._constants import REF_COUNT +from pandas.compat._constants import ( + REF_COUNT, + WARNING_CHECK_BROKEN, +) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv from pandas.errors import ( @@ -1055,8 +1058,8 @@ def _get_value(self, label, takeable: bool = False): return self.iloc[loc] def __setitem__(self, key, value) -> None: - if not PYPY: - if sys.getrefcount(self) <= 3: + if not PYPY and not WARNING_CHECK_BROKEN: + if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) @@ -3336,7 +3339,7 @@ def update(self, other: Series | Sequence | Mapping) -> None: 2 3 dtype: int64 """ - if not PYPY: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, diff --git a/pandas/tests/copy_view/test_chained_assignment_deprecation.py b/pandas/tests/copy_view/test_chained_assignment_deprecation.py index b116a99c08605..b56867ef9138f 100644 --- a/pandas/tests/copy_view/test_chained_assignment_deprecation.py +++ b/pandas/tests/copy_view/test_chained_assignment_deprecation.py @@ -1,6 +1,7 @@ import numpy as np import pytest +from pandas.compat import WARNING_CHECK_BROKEN from pandas.errors import ChainedAssignmentError from pandas import DataFrame @@ -17,6 +18,8 @@ def test_series_setitem(indexer): # using custom check instead of tm.assert_produces_warning because that doesn't # fail if multiple warnings are raised + if WARNING_CHECK_BROKEN: + return with pytest.warns() as record: # noqa: TID251 df["a"][indexer] = 0 assert len(record) == 1 From 2c932c7ed798ff6fe5acda64f1c353acdc9951da Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 12 Sep 2025 17:17:38 +0200 Subject: [PATCH 2/4] add to at/iat as well --- pandas/core/indexing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index c43a79316e1f3..2025d3ef6f44d 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2585,8 +2585,8 @@ def __getitem__(self, key): return super().__getitem__(key) def __setitem__(self, key, value) -> None: - if not PYPY: - if sys.getrefcount(self.obj) <= 2: + if not PYPY and not WARNING_CHECK_BROKEN: + if sys.getrefcount(self.obj) <= REF_COUNT: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) @@ -2616,8 +2616,8 @@ def _convert_key(self, key): return key def __setitem__(self, key, value) -> None: - if not PYPY: - if sys.getrefcount(self.obj) <= 2: + if not PYPY and not WARNING_CHECK_BROKEN: + if sys.getrefcount(self.obj) <= REF_COUNT: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) From 6447f0066a112dee8a8c9ed0aa14065bba3c9c25 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 12 Sep 2025 17:18:23 +0200 Subject: [PATCH 3/4] remove allow failure --- .github/workflows/unit-tests.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 49aaaf2a489a4..a9220ab53cbb0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -358,8 +358,6 @@ jobs: - name: Run Tests uses: ./.github/actions/run-tests - # TEMP allow this to fail until we fixed all test failures (related to chained assignment warnings) - continue-on-error: true # NOTE: this job must be kept in sync with the Pyodide build job in wheels.yml emscripten: From 95772a4fbd26120d4772b9ecb265466297d080be Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 12 Sep 2025 17:24:03 +0200 Subject: [PATCH 4/4] disabled -> broken --- pandas/_testing/contexts.py | 4 ++-- pandas/compat/__init__.py | 4 ++-- pandas/compat/_constants.py | 2 +- pandas/core/frame.py | 6 +++--- pandas/core/generic.py | 18 +++++++++--------- pandas/core/indexing.py | 8 ++++---- pandas/core/series.py | 6 +++--- .../test_chained_assignment_deprecation.py | 4 ++-- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/pandas/_testing/contexts.py b/pandas/_testing/contexts.py index b79cd797a3707..ed30b2022db10 100644 --- a/pandas/_testing/contexts.py +++ b/pandas/_testing/contexts.py @@ -14,7 +14,7 @@ from pandas.compat import ( PYPY, - WARNING_CHECK_BROKEN, + WARNING_CHECK_DISABLED, ) from pandas.errors import ChainedAssignmentError @@ -166,7 +166,7 @@ def with_csv_dialect(name: str, **kwargs) -> Generator[None]: def raises_chained_assignment_error(extra_warnings=(), extra_match=()): from pandas._testing import assert_produces_warning - if PYPY or WARNING_CHECK_BROKEN: + if PYPY or WARNING_CHECK_DISABLED: if not extra_warnings: from contextlib import nullcontext diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index bc1b0757438a4..f38abafd2db78 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -21,7 +21,7 @@ PY312, PY314, PYPY, - WARNING_CHECK_BROKEN, + WARNING_CHECK_DISABLED, WASM, ) from pandas.compat.numpy import is_numpy_dev @@ -159,7 +159,7 @@ def is_ci_environment() -> bool: "PY314", "PYARROW_MIN_VERSION", "PYPY", - "WARNING_CHECK_BROKEN", + "WARNING_CHECK_DISABLED", "WASM", "is_numpy_dev", "pa_version_under14p0", diff --git a/pandas/compat/_constants.py b/pandas/compat/_constants.py index 722c0ea785477..674afc5c62009 100644 --- a/pandas/compat/_constants.py +++ b/pandas/compat/_constants.py @@ -19,7 +19,7 @@ WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"]) ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "") REF_COUNT = 2 -WARNING_CHECK_BROKEN = PY314 +WARNING_CHECK_DISABLED = PY314 __all__ = [ diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 0911a2d25af46..e4c85f00cd620 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -52,7 +52,7 @@ from pandas.compat import PYPY from pandas.compat._constants import ( REF_COUNT, - WARNING_CHECK_BROKEN, + WARNING_CHECK_DISABLED, ) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv @@ -4299,7 +4299,7 @@ def __setitem__(self, key, value) -> None: z 3 50 # Values for 'a' and 'b' are completely ignored! """ - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -9214,7 +9214,7 @@ def update( 1 2 500.0 2 3 6.0 """ - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, diff --git a/pandas/core/generic.py b/pandas/core/generic.py index bec6c5e1b9cbe..7b91ca3d564a2 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -85,7 +85,7 @@ from pandas.compat import PYPY from pandas.compat._constants import ( REF_COUNT, - WARNING_CHECK_BROKEN, + WARNING_CHECK_DISABLED, ) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv @@ -7073,7 +7073,7 @@ def fillna( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7304,7 +7304,7 @@ def ffill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7444,7 +7444,7 @@ def bfill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7529,7 +7529,7 @@ def replace( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7892,7 +7892,7 @@ def interpolate( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -8476,7 +8476,7 @@ def clip( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10086,7 +10086,7 @@ def where( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10150,7 +10150,7 @@ def mask( ) -> Self | None: inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 2025d3ef6f44d..a0e3168f3bc6f 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -18,7 +18,7 @@ from pandas.compat import PYPY from pandas.compat._constants import ( REF_COUNT, - WARNING_CHECK_BROKEN, + WARNING_CHECK_DISABLED, ) from pandas.errors import ( AbstractMethodError, @@ -917,7 +917,7 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None) -> None: @final def __setitem__(self, key, value) -> None: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self.obj) <= REF_COUNT: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -2585,7 +2585,7 @@ def __getitem__(self, key): return super().__getitem__(key) def __setitem__(self, key, value) -> None: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self.obj) <= REF_COUNT: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -2616,7 +2616,7 @@ def _convert_key(self, key): return key def __setitem__(self, key, value) -> None: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self.obj) <= REF_COUNT: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 diff --git a/pandas/core/series.py b/pandas/core/series.py index ef4996f0ae5e4..195a6fc87cecd 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -37,7 +37,7 @@ from pandas.compat import PYPY from pandas.compat._constants import ( REF_COUNT, - WARNING_CHECK_BROKEN, + WARNING_CHECK_DISABLED, ) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv @@ -1058,7 +1058,7 @@ def _get_value(self, label, takeable: bool = False): return self.iloc[loc] def __setitem__(self, key, value) -> None: - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -3339,7 +3339,7 @@ def update(self, other: Series | Sequence | Mapping) -> None: 2 3 dtype: int64 """ - if not PYPY and not WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_DISABLED: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, diff --git a/pandas/tests/copy_view/test_chained_assignment_deprecation.py b/pandas/tests/copy_view/test_chained_assignment_deprecation.py index b56867ef9138f..f6de6af994b93 100644 --- a/pandas/tests/copy_view/test_chained_assignment_deprecation.py +++ b/pandas/tests/copy_view/test_chained_assignment_deprecation.py @@ -1,7 +1,7 @@ import numpy as np import pytest -from pandas.compat import WARNING_CHECK_BROKEN +from pandas.compat import WARNING_CHECK_DISABLED from pandas.errors import ChainedAssignmentError from pandas import DataFrame @@ -18,7 +18,7 @@ def test_series_setitem(indexer): # using custom check instead of tm.assert_produces_warning because that doesn't # fail if multiple warnings are raised - if WARNING_CHECK_BROKEN: + if WARNING_CHECK_DISABLED: return with pytest.warns() as record: # noqa: TID251 df["a"][indexer] = 0