Skip to content

Commit 83d839c

Browse files
Python 3.14 / 3.14t builds (duckdb#50)
Per [Discussion: Python 3.14 Free-Threading Support](duckdb#40), this is the first step / pre-requisite for free-threading work. This PR adds Python 3.14 & 3.14t (rc2) builds, with a test change to handle not-yet-available dependencies. #### Free Threading is not enabled by this Free threading is **not** implemented here. If DuckDB is imported in 3.14t, the GIL is enabled with the following warning: > `<frozen importlib._bootstrap>:491: RuntimeWarning: The global interpreter lock (GIL) has been enabled to load module '_duckdb', which has not declared that it can run safely without the GIL. To override this behavior and keep the GIL disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.` #### Timeline - Python 3.14 is scheduled to release on 2025-10-07: [PEP 745 – Python 3.14 Release Schedule](https://peps.python.org/pep-0745/) - In 3.14, [PEP 779: Free-threaded Python is officially supported](https://docs.python.org/3.14/whatsnew/3.14.html#pep-779-free-threaded-python-is-officially-supported) #### Changes - Update uv to 0.8.16 and cibuilwheel to 3.1.x: needed for the latest Python builds - Added Windows specific build flags for 3.14t - Add markers to disable dependencies that don't yet support 3.14 #### Notes: - Windows + 3.14t is excluded: tests fail in the CI workflow, yet work locally & in a modified workflow. Root cause is unknown. - Marked xfailed duckdb#48
2 parents 14245ff + 889699b commit 83d839c

File tree

7 files changed

+71
-31
lines changed

7 files changed

+71
-31
lines changed

.github/workflows/cleanup_pypi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
- name: Install Astral UV
5353
uses: astral-sh/setup-uv@v6
5454
with:
55-
version: "0.7.14"
55+
version: "0.8.16"
5656

5757
- name: Run Cleanup
5858
env:

.github/workflows/coverage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jobs:
7070
- name: Install Astral UV and enable the cache
7171
uses: astral-sh/setup-uv@v6
7272
with:
73-
version: "0.7.14"
73+
version: "0.8.16"
7474
python-version: 3.9
7575
enable-cache: true
7676
cache-suffix: -${{ github.workflow }}

.github/workflows/packaging_sdist.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ jobs:
5858
- name: Install Astral UV
5959
uses: astral-sh/setup-uv@v6
6060
with:
61-
version: "0.7.14"
61+
version: "0.8.16"
6262
python-version: 3.11
6363

6464
- name: Build sdist

.github/workflows/packaging_wheels.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
strategy:
3131
fail-fast: false
3232
matrix:
33-
python: [ cp39, cp310, cp311, cp312, cp313 ]
33+
python: [ cp39, cp310, cp311, cp312, cp313, cp314, cp314t ]
3434
platform:
3535
- { os: windows-2025, arch: amd64, cibw_system: win }
3636
- { os: ubuntu-24.04, arch: x86_64, cibw_system: manylinux }
@@ -45,6 +45,10 @@ jobs:
4545
- { minimal: true, python: cp311 }
4646
- { minimal: true, python: cp312 }
4747
- { minimal: true, platform: { arch: universal2 } }
48+
# Windows+cp314t disabled due to test failures in CI.
49+
# TODO: Diagnose why tests fail (access violations) in some configurations
50+
- { python: cp314t, platform: { os: windows-2025 } }
51+
4852
runs-on: ${{ matrix.platform.os }}
4953
env:
5054
CIBW_TEST_SKIP: ${{ inputs.testsuite == 'none' && '*' || '*-macosx_universal2' }}
@@ -80,16 +84,19 @@ jobs:
8084
# Install Astral UV, which will be used as build-frontend for cibuildwheel
8185
- uses: astral-sh/setup-uv@v6
8286
with:
83-
version: "0.7.14"
87+
version: "0.8.16"
8488
enable-cache: false
8589
cache-suffix: -${{ matrix.python }}-${{ matrix.platform.cibw_system }}_${{ matrix.platform.arch }}
90+
python-version: ${{ matrix.python }}
8691

8792
- name: Build${{ inputs.testsuite != 'none' && ' and test ' || ' ' }}wheels
88-
uses: pypa/cibuildwheel@v3.0
93+
uses: pypa/cibuildwheel@v3.1
8994
env:
9095
CIBW_ARCHS: ${{ matrix.platform.arch == 'amd64' && 'AMD64' || matrix.platform.arch }}
9196
CIBW_BUILD: ${{ matrix.python }}-${{ matrix.platform.cibw_system }}_${{ matrix.platform.arch }}
92-
97+
# PYTHON_GIL=1: Suppresses the RuntimeWarning that the GIL is enabled on free-threaded builds.
98+
# TODO: Remove PYTHON_GIL=1 when free-threaded is supported.
99+
CIBW_ENVIRONMENT: PYTHON_GIL=1
93100
- name: Upload wheel
94101
uses: actions/upload-artifact@v4
95102
with:

pyproject.toml

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ all = [ # users can install duckdb with 'duckdb[all]', which will install this l
4646
"ipython", # used in duckdb.query_graph
4747
"fsspec", # used in duckdb.filesystem
4848
"numpy", # used in duckdb.experimental.spark and in duckdb.fetchnumpy()
49-
"pandas", # used for pandas dataframes all over the place
50-
"pyarrow", # used for pyarrow support
51-
"adbc_driver_manager", # for the adbc driver (TODO: this should live under the duckdb package)
49+
"pandas; python_version < '3.14'", # used for pandas dataframes all over the place
50+
"pyarrow; python_version < '3.14'", # used for pyarrow support
51+
"adbc_driver_manager; python_version < '3.14'", # for the adbc driver (TODO: this should live under the duckdb package)
5252
]
5353

5454
######################################################################################################
@@ -123,7 +123,6 @@ if.env.COVERAGE = false
123123
inherit.cmake.define = "append"
124124
cmake.define.DISABLE_UNITY = "1"
125125

126-
127126
[tool.scikit-build.sdist]
128127
include = [
129128
"README.md",
@@ -204,6 +203,7 @@ required-environments = [ # ... but do always resolve for all of them
204203
"python_version >= '3.9' and sys_platform == 'linux' and platform_machine == 'x86_64'",
205204
"python_version >= '3.9' and sys_platform == 'linux' and platform_machine == 'aarch64'",
206205
]
206+
prerelease = "if-necessary-or-explicit" # for 3.14
207207

208208
# We just need pytorch for tests, wihtout GPU acceleration. PyPI doesn't host a cpu-only version for Linux, so we have
209209
# to configure the index url for cpu-only pytorch manually
@@ -219,31 +219,31 @@ torchvision = [ { index = "pytorch-cpu" } ]
219219
[dependency-groups] # used for development only, requires pip >=25.1.0
220220
stubdeps = [ # dependencies used for typehints in the stubs
221221
"fsspec",
222-
"pandas",
223-
"polars",
224-
"pyarrow",
222+
"pandas; python_version < '3.14'",
223+
"polars; python_version < '3.14'",
224+
"pyarrow; python_version < '3.14'",
225225
]
226226
test = [ # dependencies used for running tests
227227
"pytest",
228228
"pytest-reraise",
229229
"pytest-timeout",
230230
"mypy",
231231
"coverage",
232-
"gcovr",
232+
"gcovr; python_version < '3.14'",
233233
"gcsfs",
234234
"packaging",
235-
"polars",
235+
"polars; python_version < '3.14'",
236236
"psutil",
237237
"py4j",
238238
"pyotp",
239-
"pyspark",
239+
"pyspark; python_version < '3.14'",
240240
"pytz",
241241
"requests",
242242
"urllib3",
243243
"fsspec>=2022.11.0",
244-
"pandas>=2.0.0",
245-
"pyarrow>=18.0.0",
246-
"torch>=2.2.2; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'",
244+
"pandas>=2.0.0; python_version < '3.14'",
245+
"pyarrow>=18.0.0; python_version < '3.14'",
246+
"torch>=2.2.2; python_version < '3.14' and (sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13')",
247247
"tensorflow==2.14.0; sys_platform == 'darwin' and python_version < '3.12'",
248248
"tensorflow-cpu>=2.14.0; sys_platform == 'linux' and platform_machine != 'aarch64' and python_version < '3.12'",
249249
"tensorflow-cpu>=2.14.0; sys_platform == 'win32' and python_version < '3.12'",
@@ -256,10 +256,10 @@ scripts = [ # dependencies used for running scripts
256256
"ipython",
257257
"ipywidgets",
258258
"numpy",
259-
"pandas",
259+
"pandas; python_version < '3.14'",
260260
"pcpp",
261-
"polars",
262-
"pyarrow",
261+
"polars; python_version < '3.14'",
262+
"pyarrow; python_version < '3.14'",
263263
"pytz"
264264
]
265265
pypi = [ # dependencies used by the pypi cleanup script
@@ -379,6 +379,7 @@ manylinux-x86_64-image = "manylinux_2_28"
379379
manylinux-pypy_x86_64-image = "manylinux_2_28"
380380
manylinux-aarch64-image = "manylinux_2_28"
381381
manylinux-pypy_aarch64-image = "manylinux_2_28"
382+
enable = ["cpython-freethreading", "cpython-prerelease"]
382383

383384
[tool.cibuildwheel.linux]
384385
before-build = ["yum install -y ccache"]

tests/conftest.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,51 @@
11
import os
2+
import sys
23
import pytest
34
import shutil
45
from os.path import abspath, join, dirname, normpath
56
import glob
67
import duckdb
78
import warnings
89
from importlib import import_module
10+
import sys
911

1012
try:
1113
# need to ignore warnings that might be thrown deep inside pandas's import tree (from dateutil in this case)
12-
warnings.simplefilter(action='ignore', category=DeprecationWarning)
13-
pandas = import_module('pandas')
14+
warnings.simplefilter(action="ignore", category=DeprecationWarning)
15+
pandas = import_module("pandas")
1416
warnings.resetwarnings()
1517

16-
pyarrow_dtype = getattr(pandas, 'ArrowDtype', None)
18+
pyarrow_dtype = getattr(pandas, "ArrowDtype", None)
1719
except ImportError:
1820
pandas = None
1921
pyarrow_dtype = None
2022

23+
# Only install mock after we've failed to import pandas for conftest.py
24+
class MockPandas:
25+
def __getattr__(self, name):
26+
pytest.skip("pandas not available", allow_module_level=True)
27+
28+
sys.modules["pandas"] = MockPandas()
29+
sys.modules["pandas.testing"] = MockPandas()
30+
sys.modules["pandas._testing"] = MockPandas()
31+
2132
# Check if pandas has arrow dtypes enabled
22-
try:
23-
from pandas.compat import pa_version_under7p0
33+
if pandas is not None:
34+
try:
35+
from pandas.compat import pa_version_under7p0
2436

25-
pyarrow_dtypes_enabled = not pa_version_under7p0
26-
except ImportError:
37+
pyarrow_dtypes_enabled = not pa_version_under7p0
38+
except (ImportError, AttributeError):
39+
pyarrow_dtypes_enabled = False
40+
else:
2741
pyarrow_dtypes_enabled = False
2842

2943

3044
def import_pandas():
3145
if pandas:
3246
return pandas
3347
else:
34-
pytest.skip("Couldn't import pandas")
48+
pytest.skip("Couldn't import pandas", allow_module_level=True)
3549

3650

3751
# https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option
@@ -40,6 +54,23 @@ def pytest_addoption(parser):
4054
parser.addoption("--skiplist", action="append", nargs="+", type=str, help="skip listed tests")
4155

4256

57+
@pytest.hookimpl(hookwrapper=True)
58+
def pytest_runtest_call(item):
59+
"""Convert pandas requirement exceptions to skips"""
60+
61+
outcome = yield
62+
63+
# TODO: Remove skip when Pandas releases for 3.14. After, consider bumping to 3.15
64+
if sys.version_info[:2] == (3, 14):
65+
try:
66+
outcome.get_result()
67+
except duckdb.InvalidInputException as e:
68+
if "'pandas' is required for this operation but it was not installed" in str(e):
69+
pytest.skip("pandas not available - test requires pandas functionality")
70+
else:
71+
raise e
72+
73+
4374
def pytest_collection_modifyitems(config, items):
4475
tests_to_skip = config.getoption("--skiplist")
4576
if not tests_to_skip:

tests/fast/numpy/test_numpy_new_path.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Therefore, we only test the new codes and exec paths.
33
"""
44

5+
import sys
56
import numpy as np
67
import duckdb
78
from datetime import timedelta

0 commit comments

Comments
 (0)