Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ This release is compatible with NumPy 2.5.
* Fixed incorrect results of `dpnp.tensor.vecdot` in some cases with strided outputs and of `dpnp.tensor` reductions, `dpnp.tensor.vecdot` and `dpnp.tensor.matmul` on large inputs with some data types [#3082](https://github.com/IntelPython/dpnp/pull/3082)
* Fixed `dpnp.median` and `dpnp.nanmedian` raising a `ValueError` for a tuple `axis` when a kept dimension has size 0 [#3081](https://github.com/IntelPython/dpnp/pull/3081)
* Fixed a `RuntimeError` raised by `dpnp.tensor` kernels launched over more than `INT_MAX` work-items when built with DPC++ compiler 2026.2 or newer, by also passing `-fno-sycl-id-queries-fit-in-int` to the linker [#3089](https://github.com/IntelPython/dpnp/pull/3089)
* Fixed importing `dpnp.scipy.sparse` re-enabling the GIL on free-threaded Python builds, because its pybind11 extension did not declare free-threading support [#3101](https://github.com/IntelPython/dpnp/pull/3101)

### Security

Expand Down
2 changes: 1 addition & 1 deletion dpnp/backend/extensions/sparse/sparse_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ using dpnp::extensions::sparse::sparse_gemv_compute;
using dpnp::extensions::sparse::sparse_gemv_init;
using dpnp::extensions::sparse::sparse_gemv_release;

PYBIND11_MODULE(_sparse_impl, m)
PYBIND11_MODULE(_sparse_impl, m, py::mod_gil_not_used())
{
init_sparse_gemv_dispatch_tables();

Expand Down
49 changes: 49 additions & 0 deletions dpnp/tests/test_free_threading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import os
import subprocess
import sys
import sysconfig

import pytest

# Prints the first dpnp module whose import re-enables the GIL,
# "deps" if a dependency already re-enabled it, or "ok".
_IMPORT_ALL = """
import importlib, importlib.machinery, pathlib, sys
import dpctl, numpy
if sys._is_gil_enabled():
print("deps")
sys.exit()
import dpnp
if sys._is_gil_enabled():
print("dpnp")
sys.exit()
root = pathlib.Path(dpnp.__file__).parent
suffix = importlib.machinery.EXTENSION_SUFFIXES[0]
for path in sorted(root.rglob("*" + suffix)):
rel = path.relative_to(root.parent)
name = ".".join(rel.parts[:-1] + (path.name[: -len(suffix)],))
importlib.import_module(name)
if sys._is_gil_enabled():
print(name)
sys.exit()
print("ok")
"""


@pytest.mark.skipif(
not sysconfig.get_config_var("Py_GIL_DISABLED"),
reason="requires a free-threaded Python build",
)
def test_extensions_keep_gil_disabled():
env = {k: v for k, v in os.environ.items() if k != "PYTHON_GIL"}
res = subprocess.run(
[sys.executable, "-c", _IMPORT_ALL],
capture_output=True,
text=True,
env=env,
)
assert res.returncode == 0, res.stderr
out = res.stdout.strip()
if out == "deps":
pytest.skip("a dependency re-enabled the GIL")
assert out == "ok", f"importing {out} re-enabled the GIL"
Loading