Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/pypi-build-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ jobs:
# in .github/workflows/python-ci.yml to catch import-time regressions early.
CIBW_BEFORE_TEST: "uv sync --directory {project} --only-group dev --no-install-project"
CIBW_TEST_COMMAND: "uv run --directory {project} pytest tests/avro/test_decoder.py"
# Skip free-threaded (PEP 703) builds until we evaluate decoder_fast support
CIBW_SKIP: "cp3*t-*"
CIBW_FREE_THREADED_SUPPORT: "true"


- name: Add source distribution
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
max-parallel: 15
fail-fast: true
matrix:
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
python: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.14t']

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/svn-build-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ jobs:
CIBW_PROJECT_REQUIRES_PYTHON: ">=3.10,<3.15"
CIBW_BEFORE_TEST: "uv sync --directory {project} --only-group dev --no-install-project"
CIBW_TEST_COMMAND: "uv run --directory {project} pytest tests/avro/test_decoder.py"
# Skip free-threaded (PEP 703) builds until we evaluate decoder_fast support
CIBW_SKIP: "cp3*t-*"
CIBW_FREE_THREADED_SUPPORT: "true"

- name: Add source distribution
if: matrix.os == 'ubuntu-latest'
Expand Down
21 changes: 17 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ else
PYTHON_ARG =
endif

# Extras that lack free-threaded (PEP 703) wheels
FREE_THREADED_INCOMPATIBLE_EXTRAS = bodo ray sql-postgres hive-kerberos datafusion

# Detect free-threaded Python (e.g. PYTHON=3.14t) and exclude incompatible extras
ifneq ($(findstring t,$(PYTHON)),)
EXTRAS_ARG = --all-extras $(foreach extra,$(FREE_THREADED_INCOMPATIBLE_EXTRAS),--no-extra $(extra))
# Ignore test files that import excluded extras at module level
FREE_THREADED_PYTEST_IGNORES = --ignore=tests/table/test_datafusion.py --ignore=tests/table/test_upsert.py -k "not kerberos"
else
EXTRAS_ARG = --all-extras
FREE_THREADED_PYTEST_IGNORES =
endif

ifeq ($(COVERAGE),1)
TEST_RUNNER = uv run $(PYTHON_ARG) python -m coverage run --parallel-mode --source=pyiceberg -m
else
Expand Down Expand Up @@ -74,11 +87,11 @@ install-uv: ## Ensure uv is installed
fi

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

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

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

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

test-integration-exec: ## Run integration tests (excluding provision)
$(TEST_RUNNER) pytest tests/ -m integration $(PYTEST_ARGS)
$(TEST_RUNNER) pytest tests/ -m integration $(FREE_THREADED_PYTEST_IGNORES) $(PYTEST_ARGS)

test-integration-cleanup: ## Clean up integration test environment
@if [ "${KEEP_COMPOSE}" != "1" ]; then \
Expand Down
1 change: 1 addition & 0 deletions pyiceberg/avro/decoder_fast.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# cython: freethreading_compatible=True
import cython
from cython.cimports.cpython import array
from pyiceberg.avro import STRUCT_DOUBLE, STRUCT_FLOAT
Expand Down
6 changes: 5 additions & 1 deletion pyiceberg/utils/singleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
More information on metaclasses: https://docs.python.org/3/reference/datamodel.html#metaclasses
"""

import threading
from typing import Any, ClassVar


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

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

def __new__(cls, *args, **kwargs): # type: ignore
key = (cls, tuple(args), _convert_to_hashable_type(kwargs))
if key not in cls._instances:
cls._instances[key] = super().__new__(cls)
with cls._lock:
if key not in cls._instances:
cls._instances[key] = super().__new__(cls)
return cls._instances[key]

def __deepcopy__(self, memo: dict[int, Any]) -> Any:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ default-groups = [
]

[build-system]
requires = ["setuptools>=80", "wheel", "Cython>=3.0.0"]
requires = ["setuptools>=80", "wheel", "Cython>=3.1.0"]
build-backend = "setuptools.build_meta"

[tool.pytest.ini_options]
Expand Down
9 changes: 8 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# under the License.

import os
import sysconfig

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

compiler_directives = {"language_level": "3"}

# Enable free-threading support when building on free-threaded Python (PEP 703)
if sysconfig.get_config_var("Py_GIL_DISABLED"):
compiler_directives["freethreading_compatible"] = True # type: ignore[assignment]

ext_modules = cythonize(
extensions,
include_path=[package_path],
compiler_directives={"language_level": "3"},
compiler_directives=compiler_directives,
annotate=True,
)
except Exception:
Expand Down
12 changes: 6 additions & 6 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading