Skip to content

Commit 95d2ce0

Browse files
TST: run python-dev CI on 3.14-dev (#61950)
Co-authored-by: Joris Van den Bossche <[email protected]>
1 parent 452c7fb commit 95d2ce0

File tree

10 files changed

+84
-20
lines changed

10 files changed

+84
-20
lines changed

.github/workflows/unit-tests.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ jobs:
313313
# To freeze this file, uncomment out the ``if: false`` condition, and migrate the jobs
314314
# to the corresponding posix/windows-macos/sdist etc. workflows.
315315
# Feel free to modify this comment as necessary.
316-
if: false
316+
# if: false
317317
defaults:
318318
run:
319319
shell: bash -eou pipefail {0}
@@ -345,7 +345,7 @@ jobs:
345345
- name: Set up Python Dev Version
346346
uses: actions/setup-python@v6
347347
with:
348-
python-version: '3.13-dev'
348+
python-version: '3.14-dev'
349349

350350
- name: Build Environment
351351
run: |
@@ -358,6 +358,8 @@ jobs:
358358
359359
- name: Run Tests
360360
uses: ./.github/actions/run-tests
361+
# TEMP allow this to fail until we fixed all test failures (related to chained assignment warnings)
362+
continue-on-error: true
361363

362364
# NOTE: this job must be kept in sync with the Pyodide build job in wheels.yml
363365
emscripten:

pandas/compat/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
IS64,
2020
ISMUSL,
2121
PY312,
22+
PY314,
2223
PYPY,
2324
WASM,
2425
)
@@ -154,6 +155,7 @@ def is_ci_environment() -> bool:
154155
"IS64",
155156
"ISMUSL",
156157
"PY312",
158+
"PY314",
157159
"PYARROW_MIN_VERSION",
158160
"PYPY",
159161
"WASM",

pandas/compat/_constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
IS64 = sys.maxsize > 2**32
1515

1616
PY312 = sys.version_info >= (3, 12)
17+
PY314 = sys.version_info >= (3, 14)
1718
PYPY = platform.python_implementation() == "PyPy"
1819
WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"])
1920
ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "")
@@ -23,6 +24,7 @@
2324
"IS64",
2425
"ISMUSL",
2526
"PY312",
27+
"PY314",
2628
"PYPY",
2729
"WASM",
2830
]

pandas/tests/frame/test_query_eval.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def test_query_duplicate_column_name(self, engine, parser):
168168
}
169169
).rename(columns={"B": "A"})
170170

171-
res = df.query('C == 1', engine=engine, parser=parser)
171+
res = df.query("C == 1", engine=engine, parser=parser)
172172

173173
expect = DataFrame(
174174
[[1, 1, 1]],
@@ -1411,7 +1411,7 @@ def test_expr_with_column_name_with_backtick_and_hash(self):
14111411
def test_expr_with_column_name_with_backtick(self):
14121412
# GH 59285
14131413
df = DataFrame({"a`b": (1, 2, 3), "ab": (4, 5, 6)})
1414-
result = df.query("`a``b` < 2") # noqa
1414+
result = df.query("`a``b` < 2")
14151415
# Note: Formatting checks may wrongly consider the above ``inline code``.
14161416
expected = df[df["a`b"] < 2]
14171417
tm.assert_frame_equal(result, expected)

pandas/tests/indexes/test_indexing.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import numpy as np
1919
import pytest
2020

21+
from pandas.compat import PY314
2122
from pandas.errors import InvalidIndexError
2223

2324
from pandas.core.dtypes.common import (
@@ -160,13 +161,19 @@ def test_contains_requires_hashable_raises(self, index):
160161
with pytest.raises(TypeError, match=msg):
161162
[] in index
162163

164+
if PY314:
165+
container_or_iterable = "a container or iterable"
166+
else:
167+
container_or_iterable = "iterable"
168+
163169
msg = "|".join(
164170
[
165171
r"unhashable type: 'dict'",
166172
r"must be real number, not dict",
167173
r"an integer is required",
168174
r"\{\}",
169-
r"pandas\._libs\.interval\.IntervalTree' is not iterable",
175+
r"pandas\._libs\.interval\.IntervalTree' is not "
176+
f"{container_or_iterable}",
170177
]
171178
)
172179
with pytest.raises(TypeError, match=msg):

pandas/tests/io/parser/test_quoting.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import pytest
1010

11+
from pandas.compat import PY314
1112
from pandas.errors import ParserError
1213

1314
from pandas import DataFrame
@@ -20,15 +21,24 @@
2021
skip_pyarrow = pytest.mark.usefixtures("pyarrow_skip")
2122

2223

24+
if PY314:
25+
# TODO: write a regex that works with all new possitibilities here
26+
MSG1 = ""
27+
MSG2 = r"[\s\S]*"
28+
else:
29+
MSG1 = "a(n)? 1-character string"
30+
MSG2 = "string( or None)?"
31+
32+
2333
@pytest.mark.parametrize(
2434
"kwargs,msg",
2535
[
26-
({"quotechar": "foo"}, '"quotechar" must be a(n)? 1-character string'),
36+
({"quotechar": "foo"}, f'"quotechar" must be {MSG1}'),
2737
(
2838
{"quotechar": None, "quoting": csv.QUOTE_MINIMAL},
2939
"quotechar must be set if quoting enabled",
3040
),
31-
({"quotechar": 2}, '"quotechar" must be string( or None)?, not int'),
41+
({"quotechar": 2}, f'"quotechar" must be {MSG2}, not int'),
3242
],
3343
)
3444
@skip_pyarrow # ParserError: CSV parse error: Empty CSV file or block
@@ -87,8 +97,12 @@ def test_null_quote_char(all_parsers, quoting, quote_char):
8797

8898
if quoting != csv.QUOTE_NONE:
8999
# Sanity checking.
100+
if not PY314:
101+
msg = "1-character string"
102+
else:
103+
msg = "unicode character or None"
90104
msg = (
91-
'"quotechar" must be a 1-character string'
105+
f'"quotechar" must be a {msg}'
92106
if all_parsers.engine == "python" and quote_char == ""
93107
else "quotechar must be set if quoting enabled"
94108
)

pandas/tests/reshape/merge/test_merge.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import numpy as np
99
import pytest
1010

11+
from pandas.compat import PY314
12+
1113
from pandas.core.dtypes.common import (
1214
is_object_dtype,
1315
is_string_dtype,
@@ -2420,10 +2422,18 @@ def test_merge_suffix_raises(suffixes):
24202422
merge(a, b, left_index=True, right_index=True, suffixes=suffixes)
24212423

24222424

2425+
TWO_GOT_THREE = "2, got 3" if PY314 else "2"
2426+
2427+
24232428
@pytest.mark.parametrize(
24242429
"col1, col2, suffixes, msg",
24252430
[
2426-
("a", "a", ("a", "b", "c"), r"too many values to unpack \(expected 2\)"),
2431+
(
2432+
"a",
2433+
"a",
2434+
("a", "b", "c"),
2435+
(rf"too many values to unpack \(expected {TWO_GOT_THREE}\)"),
2436+
),
24272437
("a", "a", tuple("a"), r"not enough values to unpack \(expected 2, got 1\)"),
24282438
],
24292439
)

pandas/tests/scalar/period/test_period.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime
1717
from pandas._libs.tslibs.parsing import DateParseError
1818
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
19+
from pandas.compat import PY314
1920
from pandas.errors import Pandas4Warning
2021

2122
from pandas import (
@@ -347,7 +348,10 @@ def test_invalid_arguments(self):
347348
msg = '^Given date string "-2000" not likely a datetime$'
348349
with pytest.raises(ValueError, match=msg):
349350
Period("-2000", "Y")
350-
msg = "day is out of range for month"
351+
if PY314:
352+
msg = "day 0 must be in range 1..31 for month 1 in year 1: 0"
353+
else:
354+
msg = "day is out of range for month"
351355
with pytest.raises(DateParseError, match=msg):
352356
Period("0", "Y")
353357
msg = "Unknown datetime string format, unable to parse"

pandas/tests/scalar/timestamp/test_constructors.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import pytest
1818

1919
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
20+
from pandas.compat import PY314
2021
from pandas.errors import (
2122
OutOfBoundsDatetime,
2223
Pandas4Warning,
@@ -221,7 +222,10 @@ def test_constructor_positional(self):
221222
with pytest.raises(ValueError, match=msg):
222223
Timestamp(2000, 13, 1)
223224

224-
msg = "day is out of range for month"
225+
if PY314:
226+
msg = "must be in range 1..31 for month 1 in year 2000"
227+
else:
228+
msg = "day is out of range for month"
225229
with pytest.raises(ValueError, match=msg):
226230
Timestamp(2000, 1, 0)
227231
with pytest.raises(ValueError, match=msg):
@@ -245,7 +249,10 @@ def test_constructor_keyword(self):
245249
with pytest.raises(ValueError, match=msg):
246250
Timestamp(year=2000, month=13, day=1)
247251

248-
msg = "day is out of range for month"
252+
if PY314:
253+
msg = "must be in range 1..31 for month 1 in year 2000"
254+
else:
255+
msg = "day is out of range for month"
249256
with pytest.raises(ValueError, match=msg):
250257
Timestamp(year=2000, month=1, day=0)
251258
with pytest.raises(ValueError, match=msg):

pandas/tests/tools/test_to_datetime.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
iNaT,
2222
parsing,
2323
)
24-
from pandas.compat import WASM
24+
from pandas.compat import (
25+
PY314,
26+
WASM,
27+
)
2528
from pandas.errors import (
2629
OutOfBoundsDatetime,
2730
OutOfBoundsTimedelta,
@@ -57,6 +60,16 @@
5760
r"alongside this."
5861
)
5962

63+
if PY314:
64+
NOT_99 = ", not 99"
65+
DAY_IS_OUT_OF_RANGE = (
66+
r"day \d{1,2} must be in range 1\.\.\d{1,2} for "
67+
r"month \d{1,2} in year \d{4}"
68+
)
69+
else:
70+
NOT_99 = ""
71+
DAY_IS_OUT_OF_RANGE = "day is out of range for month"
72+
6073

6174
class TestTimeConversionFormats:
6275
def test_to_datetime_readonly(self, writable):
@@ -1378,7 +1391,7 @@ def test_datetime_invalid_scalar(self, value, format):
13781391
r'^Given date string "a" not likely a datetime$',
13791392
r'^unconverted data remains when parsing with format "%H:%M:%S": "9". '
13801393
f"{PARSING_ERR_MSG}$",
1381-
r"^second must be in 0..59: 00:01:99$",
1394+
rf"^second must be in 0..59{NOT_99}: 00:01:99$",
13821395
]
13831396
)
13841397
with pytest.raises(ValueError, match=msg):
@@ -1430,7 +1443,7 @@ def test_datetime_invalid_index(self, values, format):
14301443
f"{PARSING_ERR_MSG}$",
14311444
r'^unconverted data remains when parsing with format "%H:%M:%S": "9". '
14321445
f"{PARSING_ERR_MSG}$",
1433-
r"^second must be in 0..59: 00:01:99$",
1446+
rf"^second must be in 0..59{NOT_99}: 00:01:99$",
14341447
]
14351448
)
14361449
with pytest.raises(ValueError, match=msg):
@@ -2857,7 +2870,10 @@ def test_day_not_in_month_coerce(self, cache, arg, format):
28572870
assert isna(to_datetime(arg, errors="coerce", format=format, cache=cache))
28582871

28592872
def test_day_not_in_month_raise(self, cache):
2860-
msg = "day is out of range for month: 2015-02-29"
2873+
if PY314:
2874+
msg = "day 29 must be in range 1..28 for month 2 in year 2015: 2015-02-29"
2875+
else:
2876+
msg = "day is out of range for month: 2015-02-29"
28612877
with pytest.raises(ValueError, match=msg):
28622878
to_datetime("2015-02-29", errors="raise", cache=cache)
28632879

@@ -2867,12 +2883,12 @@ def test_day_not_in_month_raise(self, cache):
28672883
(
28682884
"2015-02-29",
28692885
"%Y-%m-%d",
2870-
f"^day is out of range for month. {PARSING_ERR_MSG}$",
2886+
f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$",
28712887
),
28722888
(
28732889
"2015-29-02",
28742890
"%Y-%d-%m",
2875-
f"^day is out of range for month. {PARSING_ERR_MSG}$",
2891+
f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$",
28762892
),
28772893
(
28782894
"2015-02-32",
@@ -2889,12 +2905,12 @@ def test_day_not_in_month_raise(self, cache):
28892905
(
28902906
"2015-04-31",
28912907
"%Y-%m-%d",
2892-
f"^day is out of range for month. {PARSING_ERR_MSG}$",
2908+
f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$",
28932909
),
28942910
(
28952911
"2015-31-04",
28962912
"%Y-%d-%m",
2897-
f"^day is out of range for month. {PARSING_ERR_MSG}$",
2913+
f"^{DAY_IS_OUT_OF_RANGE}. {PARSING_ERR_MSG}$",
28982914
),
28992915
],
29002916
)

0 commit comments

Comments
 (0)