Skip to content

Commit 8ad5e09

Browse files
kevinjqliuCopilot
andcommitted
initial support for 3.14t
Co-authored-by: Copilot <copilot@github.com>
1 parent 842d01c commit 8ad5e09

9 files changed

Lines changed: 41 additions & 18 deletions

File tree

‎.github/workflows/pypi-build-artifacts.yml‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ jobs:
8181
# in .github/workflows/python-ci.yml to catch import-time regressions early.
8282
CIBW_BEFORE_TEST: "uv sync --directory {project} --only-group dev --no-install-project"
8383
CIBW_TEST_COMMAND: "uv run --directory {project} pytest tests/avro/test_decoder.py"
84-
# Skip free-threaded (PEP 703) builds until we evaluate decoder_fast support
85-
CIBW_SKIP: "cp3*t-*"
84+
CIBW_FREE_THREADED_SUPPORT: "true"
8685

8786

8887
- name: Add source distribution

‎.github/workflows/python-ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
max-parallel: 15
5353
fail-fast: true
5454
matrix:
55-
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
55+
python: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.14t']
5656

5757
steps:
5858
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

‎.github/workflows/svn-build-artifacts.yml‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ jobs:
7373
CIBW_PROJECT_REQUIRES_PYTHON: ">=3.10,<3.15"
7474
CIBW_BEFORE_TEST: "uv sync --directory {project} --only-group dev --no-install-project"
7575
CIBW_TEST_COMMAND: "uv run --directory {project} pytest tests/avro/test_decoder.py"
76-
# Skip free-threaded (PEP 703) builds until we evaluate decoder_fast support
77-
CIBW_SKIP: "cp3*t-*"
76+
CIBW_FREE_THREADED_SUPPORT: "true"
7877

7978
- name: Add source distribution
8079
if: matrix.os == 'ubuntu-latest'

‎Makefile‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ else
3838
PYTHON_ARG =
3939
endif
4040

41+
# Extras that lack free-threaded (PEP 703) wheels
42+
FREE_THREADED_INCOMPATIBLE_EXTRAS = bodo ray sql-postgres hive-kerberos datafusion
43+
44+
# Detect free-threaded Python (e.g. PYTHON=3.14t) and exclude incompatible extras
45+
ifneq ($(findstring t,$(PYTHON)),)
46+
EXTRAS_ARG = --all-extras $(foreach extra,$(FREE_THREADED_INCOMPATIBLE_EXTRAS),--no-extra $(extra))
47+
# Ignore test files that import excluded extras at module level
48+
FREE_THREADED_PYTEST_IGNORES = --ignore=tests/table/test_datafusion.py --ignore=tests/table/test_upsert.py -k "not kerberos"
49+
else
50+
EXTRAS_ARG = --all-extras
51+
FREE_THREADED_PYTEST_IGNORES =
52+
endif
53+
4154
ifeq ($(COVERAGE),1)
4255
TEST_RUNNER = uv run $(PYTHON_ARG) python -m coverage run --parallel-mode --source=pyiceberg -m
4356
else
@@ -74,11 +87,11 @@ install-uv: ## Ensure uv is installed
7487
fi
7588

7689
install: install-uv ## Install uv, dependencies, and pre-commit hooks
77-
uv sync $(PYTHON_ARG) --all-extras
90+
uv sync $(PYTHON_ARG) $(EXTRAS_ARG)
7891
@# Reinstall pyiceberg if Cython extensions (.so) are missing after `make clean` (see #2869)
7992
@if ! find pyiceberg -name "*.so" 2>/dev/null | grep -q .; then \
8093
echo "Cython extensions not found, reinstalling pyiceberg..."; \
81-
uv sync $(PYTHON_ARG) --all-extras --reinstall-package pyiceberg; \
94+
uv sync $(PYTHON_ARG) $(EXTRAS_ARG) --reinstall-package pyiceberg; \
8295
fi
8396
@# Install pre-commit hooks (skipped outside git repo, e.g. release tarballs)
8497
@if [ -d .git ]; then \
@@ -104,7 +117,7 @@ lint: ## Run code linters via prek (pre-commit hooks)
104117
##@ Testing
105118

106119
test: ## Run all unit tests (excluding integration)
107-
$(TEST_RUNNER) pytest tests/ -m "(unmarked or parametrize) and not integration" $(PYTEST_ARGS)
120+
$(TEST_RUNNER) pytest tests/ -m "(unmarked or parametrize) and not integration" $(FREE_THREADED_PYTEST_IGNORES) $(PYTEST_ARGS)
108121

109122
test-integration: test-integration-setup test-integration-exec test-integration-cleanup ## Run integration tests
110123

@@ -115,7 +128,7 @@ test-integration-setup: install ## Start Docker services for integration tests
115128
uv run $(PYTHON_ARG) python dev/provision.py
116129

117130
test-integration-exec: ## Run integration tests (excluding provision)
118-
$(TEST_RUNNER) pytest tests/ -m integration $(PYTEST_ARGS)
131+
$(TEST_RUNNER) pytest tests/ -m integration $(FREE_THREADED_PYTEST_IGNORES) $(PYTEST_ARGS)
119132

120133
test-integration-cleanup: ## Clean up integration test environment
121134
@if [ "${KEEP_COMPOSE}" != "1" ]; then \

‎pyiceberg/avro/decoder_fast.pyx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17+
# cython: freethreading_compatible=True
1718
import cython
1819
from cython.cimports.cpython import array
1920
from pyiceberg.avro import STRUCT_DOUBLE, STRUCT_FLOAT

‎pyiceberg/utils/singleton.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
More information on metaclasses: https://docs.python.org/3/reference/datamodel.html#metaclasses
2929
"""
3030

31+
import threading
3132
from typing import Any, ClassVar
3233

3334

@@ -41,11 +42,14 @@ def _convert_to_hashable_type(element: Any) -> Any:
4142

4243
class Singleton:
4344
_instances: ClassVar[dict] = {} # type: ignore
45+
_lock: ClassVar[threading.Lock] = threading.Lock()
4446

4547
def __new__(cls, *args, **kwargs): # type: ignore
4648
key = (cls, tuple(args), _convert_to_hashable_type(kwargs))
4749
if key not in cls._instances:
48-
cls._instances[key] = super().__new__(cls)
50+
with cls._lock:
51+
if key not in cls._instances:
52+
cls._instances[key] = super().__new__(cls)
4953
return cls._instances[key]
5054

5155
def __deepcopy__(self, memo: dict[int, Any]) -> Any:

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ default-groups = [
148148
]
149149

150150
[build-system]
151-
requires = ["setuptools>=80", "wheel", "Cython>=3.0.0"]
151+
requires = ["setuptools>=80", "wheel", "Cython>=3.1.0"]
152152
build-backend = "setuptools.build_meta"
153153

154154
[tool.pytest.ini_options]

‎setup.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717

1818
import os
19+
import sysconfig
1920

2021
from setuptools import Extension, find_packages, setup
2122
from setuptools.command.sdist import sdist as _sdist
@@ -62,10 +63,16 @@ def make_release_tree(self, base_dir: str, files: list[str]) -> None:
6263
)
6364
]
6465

66+
compiler_directives = {"language_level": "3"}
67+
68+
# Enable free-threading support when building on free-threaded Python (PEP 703)
69+
if sysconfig.get_config_var("Py_GIL_DISABLED"):
70+
compiler_directives["freethreading_compatible"] = True # type: ignore[assignment]
71+
6572
ext_modules = cythonize(
6673
extensions,
6774
include_path=[package_path],
68-
compiler_directives={"language_level": "3"},
75+
compiler_directives=compiler_directives,
6976
annotate=True,
7077
)
7178
except Exception:

‎uv.lock‎

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)