From 373cc20a147c57dc671a441d7e57e25f91503613 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Fri, 25 Jul 2025 09:36:54 -0600 Subject: [PATCH 01/12] TST: run python-dev CI on 3.14-dev --- .github/workflows/unit-tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 7bee8474d4db5..beedcd92c467d 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -313,7 +313,6 @@ jobs: # To freeze this file, uncomment out the ``if: false`` condition, and migrate the jobs # to the corresponding posix/windows-macos/sdist etc. workflows. # Feel free to modify this comment as necessary. - if: false defaults: run: shell: bash -eou pipefail {0} @@ -345,7 +344,7 @@ jobs: - name: Set up Python Dev Version uses: actions/setup-python@v5 with: - python-version: '3.13-dev' + python-version: '3.14-dev' - name: Build Environment run: | From 05bb5eccba526be169c989ff240edc69264fa8dc Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Fri, 25 Jul 2025 13:16:40 -0600 Subject: [PATCH 02/12] MAINT: disable the warning on 3.14 and newer --- pandas/core/frame.py | 4 ++-- pandas/core/generic.py | 16 ++++++++-------- pandas/core/indexing.py | 2 +- pandas/core/series.py | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index e48620a854edb..0459c655dd393 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4296,7 +4296,7 @@ def __setitem__(self, key, value) -> None: z 3 50 # Values for 'a' and 'b' are completely ignored! """ - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= 3: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -9204,7 +9204,7 @@ def update( 1 2 500.0 2 3 6.0 """ - if not PYPY: + if not PYPY and sys.version_info < (3, 14): 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 2ae28266266f6..3b8dab6f0068b 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7069,7 +7069,7 @@ def fillna( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7300,7 +7300,7 @@ def ffill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7440,7 +7440,7 @@ def bfill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7525,7 +7525,7 @@ def replace( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7888,7 +7888,7 @@ def interpolate( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -8472,7 +8472,7 @@ def clip( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10081,7 +10081,7 @@ def where( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10145,7 +10145,7 @@ def mask( ) -> Self | None: inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): 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 42dd8adbead09..6c3e5971a4ded 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -913,7 +913,7 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None) -> None: @final def __setitem__(self, key, value) -> None: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self.obj) <= 2: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 diff --git a/pandas/core/series.py b/pandas/core/series.py index 6ae03f2464f76..abf900a094d2d 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1059,7 +1059,7 @@ def _get_value(self, label, takeable: bool = False): return self.iloc[loc] def __setitem__(self, key, value) -> None: - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= 3: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -3338,7 +3338,7 @@ def update(self, other: Series | Sequence | Mapping) -> None: 2 3 dtype: int64 """ - if not PYPY: + if not PYPY and sys.version_info < (3, 14): if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, From d7a139b5fe75da85c60dc93fc9bff76c30b78d80 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 14:32:12 -0600 Subject: [PATCH 03/12] MNT: use REF_COUNT instead of magic constants --- pandas/compat/_constants.py | 9 ++++++++- pandas/core/frame.py | 2 +- pandas/core/indexing.py | 3 ++- pandas/core/series.py | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pandas/compat/_constants.py b/pandas/compat/_constants.py index c7b7341013251..616934f66a3d3 100644 --- a/pandas/compat/_constants.py +++ b/pandas/compat/_constants.py @@ -15,10 +15,17 @@ PY311 = sys.version_info >= (3, 11) PY312 = sys.version_info >= (3, 12) +PY314 = sys.version_info >= (3, 14) 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 if PY311 else 3 +if PY311: + REF_COUNT = 2 +elif PY314: + REF_COUNT = 1 +else: + # Python 3.10 and older + REF_COUNT = 3 __all__ = [ "IS64", diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 0459c655dd393..393e934adf498 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4297,7 +4297,7 @@ def __setitem__(self, key, value) -> None: # Values for 'a' and 'b' are completely ignored! """ if not PYPY and sys.version_info < (3, 14): - if sys.getrefcount(self) <= 3: + if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 6c3e5971a4ded..24716dd875c37 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -15,6 +15,7 @@ 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 from pandas.errors import ( AbstractMethodError, ChainedAssignmentError, @@ -914,7 +915,7 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None) -> None: @final def __setitem__(self, key, value) -> None: if not PYPY and sys.version_info < (3, 14): - if sys.getrefcount(self.obj) <= 2: + 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 abf900a094d2d..c6284bcfbc374 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1060,7 +1060,7 @@ def _get_value(self, label, takeable: bool = False): def __setitem__(self, key, value) -> None: if not PYPY and sys.version_info < (3, 14): - if sys.getrefcount(self) <= 3: + if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) From b5b3d2e5b5938a673559cdb3ff4e1b43ecc965e4 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 14:39:52 -0600 Subject: [PATCH 04/12] MNT: use a global constant for disabling the warning --- pandas/compat/_constants.py | 10 +++------- pandas/core/frame.py | 6 +++--- pandas/core/generic.py | 18 +++++++++--------- pandas/core/indexing.py | 4 ++-- pandas/core/series.py | 6 +++--- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/pandas/compat/_constants.py b/pandas/compat/_constants.py index 616934f66a3d3..9f230dbeec97c 100644 --- a/pandas/compat/_constants.py +++ b/pandas/compat/_constants.py @@ -19,13 +19,9 @@ 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 "") -if PY311: - REF_COUNT = 2 -elif PY314: - REF_COUNT = 1 -else: - # Python 3.10 and older - REF_COUNT = 3 +REF_COUNT = 2 if PY311 else 3 +# hopefully there is a workaround in Python 3.14.1 +WARNING_CHECK_BROKEN = PY314 __all__ = [ "IS64", diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 393e934adf498..6cf99e9f6279a 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -49,7 +49,7 @@ 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,7 +4296,7 @@ def __setitem__(self, key, value) -> None: z 3 50 # Values for 'a' and 'b' are completely ignored! """ - if not PYPY and sys.version_info < (3, 14): + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -9204,7 +9204,7 @@ def update( 1 2 500.0 2 3 6.0 """ - if not PYPY and sys.version_info < (3, 14): + 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 3b8dab6f0068b..0768e3048238d 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -83,7 +83,7 @@ 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 ( @@ -7069,7 +7069,7 @@ def fillna( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7300,7 +7300,7 @@ def ffill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7440,7 +7440,7 @@ def bfill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7525,7 +7525,7 @@ def replace( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7888,7 +7888,7 @@ def interpolate( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -8472,7 +8472,7 @@ def clip( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10081,7 +10081,7 @@ def where( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10145,7 +10145,7 @@ def mask( ) -> Self | None: inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and 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 24716dd875c37..f6cb82c239edc 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -15,7 +15,7 @@ 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 +from pandas.compat._constants import REF_COUNT, WARNING_CHECK_BROKEN from pandas.errors import ( AbstractMethodError, ChainedAssignmentError, @@ -914,7 +914,7 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None) -> None: @final def __setitem__(self, key, value) -> None: - if not PYPY and sys.version_info < (3, 14): + 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 c6284bcfbc374..2c7c7ca8ee11e 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -34,7 +34,7 @@ ) 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 ( @@ -1059,7 +1059,7 @@ def _get_value(self, label, takeable: bool = False): return self.iloc[loc] def __setitem__(self, key, value) -> None: - if not PYPY and sys.version_info < (3, 14): + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT + 1: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 @@ -3338,7 +3338,7 @@ def update(self, other: Series | Sequence | Mapping) -> None: 2 3 dtype: int64 """ - if not PYPY and sys.version_info < (3, 14): + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, From d8e15e3670dc74b0908377e0c1b94dfd84161c87 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 14:46:23 -0600 Subject: [PATCH 05/12] TST: disable checks for ChainedAssignmentError in tests --- pandas/_testing/contexts.py | 4 ++-- pandas/compat/__init__.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pandas/_testing/contexts.py b/pandas/_testing/contexts.py index da147c117ad43..37d631ea2fa0f 100644 --- a/pandas/_testing/contexts.py +++ b/pandas/_testing/contexts.py @@ -12,7 +12,7 @@ ) 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 +163,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 d5dbcb74d29e4..9ccf20f6f8640 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -20,8 +20,10 @@ ISMUSL, PY311, PY312, + PY314, PYPY, WASM, + WARNING_CHECK_BROKEN, ) from pandas.compat.numpy import is_numpy_dev from pandas.compat.pyarrow import ( @@ -157,7 +159,9 @@ def is_ci_environment() -> bool: "ISMUSL", "PY311", "PY312", + "PY314", "PYPY", + "WARNING_CHECK_BROKEN", "WASM", "is_numpy_dev", "pa_version_under12p1", From a1a5a6bf8283b2597f9cdc11f117ca552fa0a233 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 14:49:37 -0600 Subject: [PATCH 06/12] MNT: appease isort --- pandas/_testing/contexts.py | 5 ++++- pandas/compat/__init__.py | 2 +- pandas/core/frame.py | 5 ++++- pandas/core/generic.py | 5 ++++- pandas/core/indexing.py | 5 ++++- pandas/core/series.py | 5 ++++- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pandas/_testing/contexts.py b/pandas/_testing/contexts.py index 37d631ea2fa0f..b79cd797a3707 100644 --- a/pandas/_testing/contexts.py +++ b/pandas/_testing/contexts.py @@ -12,7 +12,10 @@ ) import uuid -from pandas.compat import PYPY, WARNING_CHECK_BROKEN +from pandas.compat import ( + PYPY, + WARNING_CHECK_BROKEN, +) from pandas.errors import ChainedAssignmentError from pandas.io.common import get_handle diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index 9ccf20f6f8640..be614c249fe87 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -22,8 +22,8 @@ PY312, PY314, PYPY, - WASM, WARNING_CHECK_BROKEN, + WASM, ) from pandas.compat.numpy import is_numpy_dev from pandas.compat.pyarrow import ( diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 6cf99e9f6279a..3f1b8517fcc7f 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -49,7 +49,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, WARNING_CHECK_BROKEN +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 ( diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 0768e3048238d..89c58f2e5ccfd 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, WARNING_CHECK_BROKEN +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 ( diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index f6cb82c239edc..6a796c92977af 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -15,7 +15,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.compat._constants import ( + REF_COUNT, + WARNING_CHECK_BROKEN, +) from pandas.errors import ( AbstractMethodError, ChainedAssignmentError, diff --git a/pandas/core/series.py b/pandas/core/series.py index 2c7c7ca8ee11e..9242d47167f1c 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -34,7 +34,10 @@ ) from pandas._libs.lib import is_range_indexer from pandas.compat import PYPY -from pandas.compat._constants import REF_COUNT, WARNING_CHECK_BROKEN +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 ( From 979135fab05e6f6a3140561f8239ae27911d6f8c Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 15:13:36 -0600 Subject: [PATCH 07/12] BUG: fix WARNING_CHECK_BROKEN checks --- pandas/core/generic.py | 16 ++++++++-------- .../test_chained_assignment_deprecation.py | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 89c58f2e5ccfd..de3e506866341 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7072,7 +7072,7 @@ def fillna( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7303,7 +7303,7 @@ def ffill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7443,7 +7443,7 @@ def bfill( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7528,7 +7528,7 @@ def replace( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -7891,7 +7891,7 @@ def interpolate( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -8475,7 +8475,7 @@ def clip( inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10084,7 +10084,7 @@ def where( """ inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + if not PYPY and not WARNING_CHECK_BROKEN: if sys.getrefcount(self) <= REF_COUNT: warnings.warn( _chained_assignment_method_msg, @@ -10148,7 +10148,7 @@ def mask( ) -> Self | None: inplace = validate_bool_kwarg(inplace, "inplace") if inplace: - if not PYPY and WARNING_CHECK_BROKEN: + 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 4aef69a6fde98..8aa7696c9275e 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 180081b04fa9c18ebec787b1c40b321f93a0dce2 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 16:20:02 -0600 Subject: [PATCH 08/12] MNT: handle changes in error messages in Python 3.14 --- pandas/tests/indexes/test_indexing.py | 9 ++++++- pandas/tests/io/parser/test_quoting.py | 20 +++++++++++--- pandas/tests/reshape/merge/test_merge.py | 5 +++- pandas/tests/scalar/period/test_period.py | 6 ++++- .../scalar/timestamp/test_constructors.py | 16 +++++++++--- pandas/tests/tools/test_to_datetime.py | 26 +++++++++++++------ 6 files changed, 64 insertions(+), 18 deletions(-) diff --git a/pandas/tests/indexes/test_indexing.py b/pandas/tests/indexes/test_indexing.py index 1bbffcee3b671..c1c9f0fde863d 100644 --- a/pandas/tests/indexes/test_indexing.py +++ b/pandas/tests/indexes/test_indexing.py @@ -18,6 +18,7 @@ import numpy as np import pytest +from pandas.compat import PY314 from pandas.errors import InvalidIndexError from pandas.core.dtypes.common import ( @@ -160,13 +161,19 @@ def test_contains_requires_hashable_raises(self, index): with pytest.raises(TypeError, match=msg): [] in index + if PY314: + container_or_iterable = "a container or iterable" + else: + container_or_iterable = "iterable" + msg = "|".join( [ r"unhashable type: 'dict'", r"must be real number, not dict", r"an integer is required", r"\{\}", - r"pandas\._libs\.interval\.IntervalTree' is not iterable", + r"pandas\._libs\.interval\.IntervalTree' is not " + f"{container_or_iterable}", ] ) with pytest.raises(TypeError, match=msg): diff --git a/pandas/tests/io/parser/test_quoting.py b/pandas/tests/io/parser/test_quoting.py index a70b7e3389c1b..16381b4c2e42e 100644 --- a/pandas/tests/io/parser/test_quoting.py +++ b/pandas/tests/io/parser/test_quoting.py @@ -8,7 +8,7 @@ import pytest -from pandas.compat import PY311 +from pandas.compat import PY311, PY314 from pandas.errors import ParserError from pandas import DataFrame @@ -21,15 +21,23 @@ skip_pyarrow = pytest.mark.usefixtures("pyarrow_skip") +if PY314: + # TODO: write a regex that works with all new possitibilities here + MSG1 = "" + MSG2 = "[\s\S]*" +else: + MSG1 = "a(n)? 1-character string" + MSG2 = "string( or None)?" + @pytest.mark.parametrize( "kwargs,msg", [ - ({"quotechar": "foo"}, '"quotechar" must be a(n)? 1-character string'), + ({"quotechar": "foo"}, f'"quotechar" must be {MSG1}'), ( {"quotechar": None, "quoting": csv.QUOTE_MINIMAL}, "quotechar must be set if quoting enabled", ), - ({"quotechar": 2}, '"quotechar" must be string( or None)?, not int'), + ({"quotechar": 2}, f'"quotechar" must be {MSG2}, not int'), ], ) @skip_pyarrow # ParserError: CSV parse error: Empty CSV file or block @@ -88,8 +96,12 @@ def test_null_quote_char(all_parsers, quoting, quote_char): if quoting != csv.QUOTE_NONE: # Sanity checking. + if not PY314: + msg = "1-character string" + else: + msg = "unicode character or None" msg = ( - '"quotechar" must be a 1-character string' + f'"quotechar" must be a {msg}' if PY311 and all_parsers.engine == "python" and quote_char == "" else "quotechar must be set if quoting enabled" ) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index a8e29ef03acc2..a5ea06399d09e 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -8,6 +8,7 @@ import numpy as np import pytest +from pandas.compat import PY314 from pandas.core.dtypes.common import ( is_object_dtype, is_string_dtype, @@ -2420,10 +2421,12 @@ def test_merge_suffix_raises(suffixes): merge(a, b, left_index=True, right_index=True, suffixes=suffixes) +TWO_GOT_THREE = "2, got 3" if PY314 else "2" + @pytest.mark.parametrize( "col1, col2, suffixes, msg", [ - ("a", "a", ("a", "b", "c"), r"too many values to unpack \(expected 2\)"), + ("a", "a", ("a", "b", "c"), fr"too many values to unpack \(expected {TWO_GOT_THREE}\)"), ("a", "a", tuple("a"), r"not enough values to unpack \(expected 2, got 1\)"), ], ) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index baaedaa853565..5de2a0780880b 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -8,6 +8,7 @@ import numpy as np import pytest +from pandas.compat import PY314 from pandas._libs.tslibs import iNaT from pandas._libs.tslibs.ccalendar import ( DAYS, @@ -344,7 +345,10 @@ def test_invalid_arguments(self): msg = '^Given date string "-2000" not likely a datetime$' with pytest.raises(ValueError, match=msg): Period("-2000", "Y") - msg = "day is out of range for month" + if PY314: + msg = 'day 0 must be in range 1..31 for month 1 in year 1: 0' + else: + msg = "day is out of range for month" with pytest.raises(DateParseError, match=msg): Period("0", "Y") msg = "Unknown datetime string format, unable to parse" diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 09ca5a71503ad..197d1c1fd6461 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -16,6 +16,7 @@ import numpy as np import pytest +from pandas.compat import PY314 from pandas._libs.tslibs.dtypes import NpyDatetimeUnit from pandas.errors import OutOfBoundsDatetime @@ -212,13 +213,19 @@ def test_constructor_positional(self): with pytest.raises(TypeError, match=msg): Timestamp(2000, 1) - msg = "month must be in 1..12" + if PY314: + msg = 'month must be in 1..12, not' + else: + msg = "day is out of range for month" with pytest.raises(ValueError, match=msg): Timestamp(2000, 0, 1) with pytest.raises(ValueError, match=msg): Timestamp(2000, 13, 1) - msg = "day is out of range for month" + if PY314: + msg = "must be in range 1..31 for month 1 in year 2000" + else: + msg = "day is out of range for month" with pytest.raises(ValueError, match=msg): Timestamp(2000, 1, 0) with pytest.raises(ValueError, match=msg): @@ -242,7 +249,10 @@ def test_constructor_keyword(self): with pytest.raises(ValueError, match=msg): Timestamp(year=2000, month=13, day=1) - msg = "day is out of range for month" + if PY314: + msg = "must be in range 1..31 for month 1 in year 2000" + else: + msg = "day is out of range for month" with pytest.raises(ValueError, match=msg): Timestamp(year=2000, month=1, day=0) with pytest.raises(ValueError, match=msg): diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py index 9bc88a7e0a824..85bb65ab4f12b 100644 --- a/pandas/tests/tools/test_to_datetime.py +++ b/pandas/tests/tools/test_to_datetime.py @@ -21,7 +21,7 @@ iNaT, parsing, ) -from pandas.compat import WASM +from pandas.compat import WASM, PY314 from pandas.errors import ( OutOfBoundsDatetime, OutOfBoundsTimedelta, @@ -57,6 +57,13 @@ r"alongside this." ) +if PY314: + NOT_99 = ", not 99" + DAY_IS_OUT_OF_RANGE = "day \d{1,2} must be in range 1\.\.\d{1,2} for month \d{1,2} in year \d{4}" +else: + NOT_99 = "" + DAY_IS_OUT_OF_RANGE = "day is out of range for month" + class TestTimeConversionFormats: def test_to_datetime_readonly(self, writable): @@ -1372,7 +1379,7 @@ def test_datetime_invalid_scalar(self, value, format): r'^Given date string "a" not likely a datetime$', r'^unconverted data remains when parsing with format "%H:%M:%S": "9". ' f"{PARSING_ERR_MSG}$", - r"^second must be in 0..59: 00:01:99$", + fr"^second must be in 0..59{NOT_99}: 00:01:99$", ] ) with pytest.raises(ValueError, match=msg): @@ -1424,7 +1431,7 @@ def test_datetime_invalid_index(self, values, format): f"{PARSING_ERR_MSG}$", r'^unconverted data remains when parsing with format "%H:%M:%S": "9". ' f"{PARSING_ERR_MSG}$", - r"^second must be in 0..59: 00:01:99$", + fr"^second must be in 0..59{NOT_99}: 00:01:99$", ] ) with pytest.raises(ValueError, match=msg): @@ -2851,7 +2858,10 @@ def test_day_not_in_month_coerce(self, cache, arg, format): assert isna(to_datetime(arg, errors="coerce", format=format, cache=cache)) def test_day_not_in_month_raise(self, cache): - msg = "day is out of range for month: 2015-02-29" + if PY314: + msg = "day 29 must be in range 1..28 for month 2 in year 2015: 2015-02-29" + else: + msg = "day is out of range for month: 2015-02-29" with pytest.raises(ValueError, match=msg): to_datetime("2015-02-29", errors="raise", cache=cache) @@ -2861,12 +2871,12 @@ def test_day_not_in_month_raise(self, cache): ( "2015-02-29", "%Y-%m-%d", - f"^day is out of range for month. {PARSING_ERR_MSG}$", + f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$", ), ( "2015-29-02", "%Y-%d-%m", - f"^day is out of range for month. {PARSING_ERR_MSG}$", + f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$", ), ( "2015-02-32", @@ -2883,12 +2893,12 @@ def test_day_not_in_month_raise(self, cache): ( "2015-04-31", "%Y-%m-%d", - f"^day is out of range for month. {PARSING_ERR_MSG}$", + f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$", ), ( "2015-31-04", "%Y-%d-%m", - f"^day is out of range for month. {PARSING_ERR_MSG}$", + f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$", ), ], ) From a1b9a55a006f98f387d834365d3a076b95785027 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 16:20:12 -0600 Subject: [PATCH 09/12] MNT: migrate from codecs.open to open --- pandas/tests/io/formats/test_to_latex.py | 4 ++-- pandas/tests/io/test_common.py | 4 ++-- pandas/util/_print_versions.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/tests/io/formats/test_to_latex.py b/pandas/tests/io/formats/test_to_latex.py index ebc6ff5be108f..e67fda3a75ba7 100644 --- a/pandas/tests/io/formats/test_to_latex.py +++ b/pandas/tests/io/formats/test_to_latex.py @@ -42,7 +42,7 @@ def test_to_latex_to_file_utf8_with_encoding(self): df = DataFrame([["au\xdfgangen"]]) with tm.ensure_clean("test.tex") as path: df.to_latex(path, encoding="utf-8") - with codecs.open(path, "r", encoding="utf-8") as f: + with open(path, "r", encoding="utf-8") as f: assert df.to_latex() == f.read() def test_to_latex_to_file_utf8_without_encoding(self): @@ -50,7 +50,7 @@ def test_to_latex_to_file_utf8_without_encoding(self): df = DataFrame([["au\xdfgangen"]]) with tm.ensure_clean("test.tex") as path: df.to_latex(path) - with codecs.open(path, "r", encoding="utf-8") as f: + with open(path, "r", encoding="utf-8") as f: assert df.to_latex() == f.read() def test_to_latex_tabular_with_index(self): diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index 4a5e41397b59d..0d5e00dfdc245 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -523,9 +523,9 @@ def test_codecs_encoding(encoding, format): index=pd.Index([f"i-{i}" for i in range(30)]), ) with tm.ensure_clean() as path: - with codecs.open(path, mode="w", encoding=encoding) as handle: + with open(path, mode="w", encoding=encoding) as handle: getattr(expected, f"to_{format}")(handle) - with codecs.open(path, mode="r", encoding=encoding) as handle: + with open(path, mode="r", encoding=encoding) as handle: if format == "csv": df = pd.read_csv(handle, index_col=0) else: diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py index bd20660bdbba6..3abe723a2b558 100644 --- a/pandas/util/_print_versions.py +++ b/pandas/util/_print_versions.py @@ -143,7 +143,7 @@ def show_versions(as_json: str | bool = False) -> None: sys.stdout.writelines(json.dumps(j, indent=2)) else: assert isinstance(as_json, str) # needed for mypy - with codecs.open(as_json, "wb", encoding="utf8") as f: + with open(as_json, "w", encoding="utf-8") as f: json.dump(j, f, indent=2) else: From 9bd7bbb734e10a39cced6613d48211b9578071e6 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 16:49:17 -0600 Subject: [PATCH 10/12] MNT: fix linters --- pandas/tests/frame/test_query_eval.py | 4 ++-- pandas/tests/io/formats/test_to_latex.py | 1 - pandas/tests/io/parser/test_quoting.py | 7 +++++-- pandas/tests/reshape/merge/test_merge.py | 4 +++- pandas/tests/scalar/period/test_period.py | 4 ++-- pandas/tests/scalar/timestamp/test_constructors.py | 4 ++-- pandas/tests/tools/test_to_datetime.py | 8 ++++++-- pandas/util/_print_versions.py | 1 - 8 files changed, 20 insertions(+), 13 deletions(-) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index f93105498ac79..b599be5d042fe 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -168,7 +168,7 @@ def test_query_duplicate_column_name(self, engine, parser): } ).rename(columns={"B": "A"}) - res = df.query('C == 1', engine=engine, parser=parser) + res = df.query("C == 1", engine=engine, parser=parser) expect = DataFrame( [[1, 1, 1]], @@ -1411,7 +1411,7 @@ def test_expr_with_column_name_with_backtick_and_hash(self): def test_expr_with_column_name_with_backtick(self): # GH 59285 df = DataFrame({"a`b": (1, 2, 3), "ab": (4, 5, 6)}) - result = df.query("`a``b` < 2") # noqa + result = df.query("`a``b` < 2") # Note: Formatting checks may wrongly consider the above ``inline code``. expected = df[df["a`b"] < 2] tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/io/formats/test_to_latex.py b/pandas/tests/io/formats/test_to_latex.py index e67fda3a75ba7..75dd47f042a95 100644 --- a/pandas/tests/io/formats/test_to_latex.py +++ b/pandas/tests/io/formats/test_to_latex.py @@ -1,4 +1,3 @@ -import codecs from datetime import datetime from textwrap import dedent diff --git a/pandas/tests/io/parser/test_quoting.py b/pandas/tests/io/parser/test_quoting.py index 16381b4c2e42e..3485817f1ce59 100644 --- a/pandas/tests/io/parser/test_quoting.py +++ b/pandas/tests/io/parser/test_quoting.py @@ -8,7 +8,10 @@ import pytest -from pandas.compat import PY311, PY314 +from pandas.compat import ( + PY311, + PY314, +) from pandas.errors import ParserError from pandas import DataFrame @@ -24,7 +27,7 @@ if PY314: # TODO: write a regex that works with all new possitibilities here MSG1 = "" - MSG2 = "[\s\S]*" + MSG2 = r"[\s\S]*" else: MSG1 = "a(n)? 1-character string" MSG2 = "string( or None)?" diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index a5ea06399d09e..bcc8ef14ad6f3 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -9,6 +9,7 @@ import pytest from pandas.compat import PY314 + from pandas.core.dtypes.common import ( is_object_dtype, is_string_dtype, @@ -2426,7 +2427,8 @@ def test_merge_suffix_raises(suffixes): @pytest.mark.parametrize( "col1, col2, suffixes, msg", [ - ("a", "a", ("a", "b", "c"), fr"too many values to unpack \(expected {TWO_GOT_THREE}\)"), + ("a", "a", ("a", "b", "c"), ( + fr"too many values to unpack \(expected {TWO_GOT_THREE}\)")), ("a", "a", tuple("a"), r"not enough values to unpack \(expected 2, got 1\)"), ], ) diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index 5de2a0780880b..d8404937dcf97 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -8,7 +8,6 @@ import numpy as np import pytest -from pandas.compat import PY314 from pandas._libs.tslibs import iNaT from pandas._libs.tslibs.ccalendar import ( DAYS, @@ -17,6 +16,7 @@ from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime from pandas._libs.tslibs.parsing import DateParseError from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG +from pandas.compat import PY314 from pandas import ( NaT, @@ -346,7 +346,7 @@ def test_invalid_arguments(self): with pytest.raises(ValueError, match=msg): Period("-2000", "Y") if PY314: - msg = 'day 0 must be in range 1..31 for month 1 in year 1: 0' + msg = "day 0 must be in range 1..31 for month 1 in year 1: 0" else: msg = "day is out of range for month" with pytest.raises(DateParseError, match=msg): diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 197d1c1fd6461..dcb5fb94511e9 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -16,8 +16,8 @@ import numpy as np import pytest -from pandas.compat import PY314 from pandas._libs.tslibs.dtypes import NpyDatetimeUnit +from pandas.compat import PY314 from pandas.errors import OutOfBoundsDatetime from pandas import ( @@ -214,7 +214,7 @@ def test_constructor_positional(self): Timestamp(2000, 1) if PY314: - msg = 'month must be in 1..12, not' + msg = "month must be in 1..12, not" else: msg = "day is out of range for month" with pytest.raises(ValueError, match=msg): diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py index 85bb65ab4f12b..8262ff6a52b99 100644 --- a/pandas/tests/tools/test_to_datetime.py +++ b/pandas/tests/tools/test_to_datetime.py @@ -21,7 +21,10 @@ iNaT, parsing, ) -from pandas.compat import WASM, PY314 +from pandas.compat import ( + PY314, + WASM, +) from pandas.errors import ( OutOfBoundsDatetime, OutOfBoundsTimedelta, @@ -59,7 +62,8 @@ if PY314: NOT_99 = ", not 99" - DAY_IS_OUT_OF_RANGE = "day \d{1,2} must be in range 1\.\.\d{1,2} for month \d{1,2} in year \d{4}" + DAY_IS_OUT_OF_RANGE = (r"day \d{1,2} must be in range 1\.\.\d{1,2} for " + r"month \d{1,2} in year \d{4}") else: NOT_99 = "" DAY_IS_OUT_OF_RANGE = "day is out of range for month" diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py index 3abe723a2b558..efcb50bf4345c 100644 --- a/pandas/util/_print_versions.py +++ b/pandas/util/_print_versions.py @@ -1,6 +1,5 @@ from __future__ import annotations -import codecs import json import locale import os From da5dcabc493ec1cba1fd3d08ef7cee85573a666d Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 17:56:05 -0600 Subject: [PATCH 11/12] MNT: fix pre-commit --- pandas/tests/io/formats/test_to_latex.py | 4 ++-- pandas/tests/io/parser/test_quoting.py | 1 + pandas/tests/io/test_common.py | 2 +- pandas/tests/reshape/merge/test_merge.py | 9 +++++++-- pandas/tests/tools/test_to_datetime.py | 10 ++++++---- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pandas/tests/io/formats/test_to_latex.py b/pandas/tests/io/formats/test_to_latex.py index 75dd47f042a95..82f36c94801e3 100644 --- a/pandas/tests/io/formats/test_to_latex.py +++ b/pandas/tests/io/formats/test_to_latex.py @@ -41,7 +41,7 @@ def test_to_latex_to_file_utf8_with_encoding(self): df = DataFrame([["au\xdfgangen"]]) with tm.ensure_clean("test.tex") as path: df.to_latex(path, encoding="utf-8") - with open(path, "r", encoding="utf-8") as f: + with open(path, encoding="utf-8") as f: assert df.to_latex() == f.read() def test_to_latex_to_file_utf8_without_encoding(self): @@ -49,7 +49,7 @@ def test_to_latex_to_file_utf8_without_encoding(self): df = DataFrame([["au\xdfgangen"]]) with tm.ensure_clean("test.tex") as path: df.to_latex(path) - with open(path, "r", encoding="utf-8") as f: + with open(path, encoding="utf-8") as f: assert df.to_latex() == f.read() def test_to_latex_tabular_with_index(self): diff --git a/pandas/tests/io/parser/test_quoting.py b/pandas/tests/io/parser/test_quoting.py index 3485817f1ce59..261003d94ddf0 100644 --- a/pandas/tests/io/parser/test_quoting.py +++ b/pandas/tests/io/parser/test_quoting.py @@ -32,6 +32,7 @@ MSG1 = "a(n)? 1-character string" MSG2 = "string( or None)?" + @pytest.mark.parametrize( "kwargs,msg", [ diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index 0d5e00dfdc245..5ee82268a87d8 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -525,7 +525,7 @@ def test_codecs_encoding(encoding, format): with tm.ensure_clean() as path: with open(path, mode="w", encoding=encoding) as handle: getattr(expected, f"to_{format}")(handle) - with open(path, mode="r", encoding=encoding) as handle: + with open(path, encoding=encoding) as handle: if format == "csv": df = pd.read_csv(handle, index_col=0) else: diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index bcc8ef14ad6f3..2d16c0fe452e0 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -2424,11 +2424,16 @@ def test_merge_suffix_raises(suffixes): TWO_GOT_THREE = "2, got 3" if PY314 else "2" + @pytest.mark.parametrize( "col1, col2, suffixes, msg", [ - ("a", "a", ("a", "b", "c"), ( - fr"too many values to unpack \(expected {TWO_GOT_THREE}\)")), + ( + "a", + "a", + ("a", "b", "c"), + (rf"too many values to unpack \(expected {TWO_GOT_THREE}\)"), + ), ("a", "a", tuple("a"), r"not enough values to unpack \(expected 2, got 1\)"), ], ) diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py index 8262ff6a52b99..3df686e2c6d1a 100644 --- a/pandas/tests/tools/test_to_datetime.py +++ b/pandas/tests/tools/test_to_datetime.py @@ -62,8 +62,10 @@ if PY314: NOT_99 = ", not 99" - DAY_IS_OUT_OF_RANGE = (r"day \d{1,2} must be in range 1\.\.\d{1,2} for " - r"month \d{1,2} in year \d{4}") + DAY_IS_OUT_OF_RANGE = ( + r"day \d{1,2} must be in range 1\.\.\d{1,2} for " + r"month \d{1,2} in year \d{4}" + ) else: NOT_99 = "" DAY_IS_OUT_OF_RANGE = "day is out of range for month" @@ -1383,7 +1385,7 @@ def test_datetime_invalid_scalar(self, value, format): r'^Given date string "a" not likely a datetime$', r'^unconverted data remains when parsing with format "%H:%M:%S": "9". ' f"{PARSING_ERR_MSG}$", - fr"^second must be in 0..59{NOT_99}: 00:01:99$", + rf"^second must be in 0..59{NOT_99}: 00:01:99$", ] ) with pytest.raises(ValueError, match=msg): @@ -1435,7 +1437,7 @@ def test_datetime_invalid_index(self, values, format): f"{PARSING_ERR_MSG}$", r'^unconverted data remains when parsing with format "%H:%M:%S": "9". ' f"{PARSING_ERR_MSG}$", - fr"^second must be in 0..59{NOT_99}: 00:01:99$", + rf"^second must be in 0..59{NOT_99}: 00:01:99$", ] ) with pytest.raises(ValueError, match=msg): From c547b6b94009330f6a3db58e0e692b3fe4003680 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Aug 2025 17:58:55 -0600 Subject: [PATCH 12/12] TST: avoid EncodingWarning --- pandas/tests/io/test_common.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index 5ee82268a87d8..9f373f30c1817 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -513,9 +513,8 @@ def test_is_fsspec_url_chained(): assert not icom.is_fsspec_url("filecache::://pandas/test.csv") -@pytest.mark.parametrize("encoding", [None, "utf-8"]) @pytest.mark.parametrize("format", ["csv", "json"]) -def test_codecs_encoding(encoding, format): +def test_codecs_encoding(format): # GH39247 expected = pd.DataFrame( 1.1 * np.arange(120).reshape((30, 4)), @@ -523,9 +522,9 @@ def test_codecs_encoding(encoding, format): index=pd.Index([f"i-{i}" for i in range(30)]), ) with tm.ensure_clean() as path: - with open(path, mode="w", encoding=encoding) as handle: + with open(path, mode="w", encoding="utf-8") as handle: getattr(expected, f"to_{format}")(handle) - with open(path, encoding=encoding) as handle: + with open(path, encoding='utf-8') as handle: if format == "csv": df = pd.read_csv(handle, index_col=0) else: