diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34b381a8d5..f27d086b77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,7 @@ jobs: # python-version: "3.12" # QT_API: PyQt5 # with_opencl: true - - os: macos-13 + - os: macos-latest python-version: "3.13" QT_API: PyQt6 with_opencl: true diff --git a/src/silx/conftest.py b/src/silx/conftest.py index 0a6a84e6d0..ca1173e110 100644 --- a/src/silx/conftest.py +++ b/src/silx/conftest.py @@ -77,12 +77,21 @@ def pytest_configure(config): "ignore:Non-empty compiler output encountered. Set the environment variable PYOPENCL_COMPILER_OUTPUT=1 to see more.:UserWarning", # Remove __array__ ignore once h5py v3.12 is released "ignore:__array__ implementation doesn't accept a copy keyword, so passing copy=False failed. __array__ must implement 'dtype' and 'copy' keyword arguments.:DeprecationWarning", - "ignore::pyopencl.RepeatedKernelRetrieval", # Deprecated pyparsing usage in matplotlib: https://github.com/matplotlib/matplotlib/issues/30617 "ignore::DeprecationWarning:matplotlib._fontconfig_pattern", "ignore::DeprecationWarning:matplotlib._mathtext", "ignore::DeprecationWarning:pyparsing.util", ) +try: + import pyopencl +except Exception: + pass +else: + pyopen_version = tuple(int(i) for i in pyopencl.__version__.split(".")[:2]) + if pyopen_version >= (2025, 2): + _FILTERWARNINGS = _FILTERWARNINGS + ( + "ignore::pyopencl.RepeatedKernelRetrieval", + ) def pytest_collection_modifyitems(items): diff --git a/src/silx/io/test/test_nxdata.py b/src/silx/io/test/test_nxdata.py index baec00c269..bc7d19f217 100644 --- a/src/silx/io/test/test_nxdata.py +++ b/src/silx/io/test/test_nxdata.py @@ -24,9 +24,10 @@ __authors__ = ["P. Knobel"] __license__ = "MIT" -__date__ = "24/03/2020" +__date__ = "02/12/2025" +import os import tempfile import unittest @@ -490,6 +491,12 @@ def setUp(self): tmp.file.close() self.h5fname = tmp.name + def tearDown(self): + try: + os.unlink(self.h5fname) + except Exception as err: + print(f"{type(err).__name__}: {err}\nWhile deleting `{self.h5fname}`") + def testSimpleSave(self): sig = numpy.array([0, 1, 2]) a0 = numpy.array([2, 3, 4]) diff --git a/src/silx/opencl/processing.py b/src/silx/opencl/processing.py index 0ace8653f4..b4ff6ed4a0 100644 --- a/src/silx/opencl/processing.py +++ b/src/silx/opencl/processing.py @@ -3,7 +3,7 @@ # Project: S I L X project # https://github.com/silx-kit/silx # -# Copyright (C) 2012-2023 European Synchrotron Radiation Facility, Grenoble, France +# Copyright (C) 2012-2025 European Synchrotron Radiation Facility, Grenoble, France # # Principal author: Jérôme Kieffer (Jerome.Kieffer@ESRF.eu) # @@ -37,7 +37,7 @@ __contact__ = "Jerome.Kieffer@ESRF.eu" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -__date__ = "20/11/2024" +__date__ = "08/12/2025" __status__ = "stable" import os @@ -67,7 +67,10 @@ class KernelContainer: - """Those object holds a copy of all kernels accessible as attributes""" + """Those object holds a copy of all kernels accessible as attributes + + Allows a work around for this issue: https://github.com/inducer/pyopencl/issues/830 + """ def __init__(self, program): """Constructor of the class @@ -75,35 +78,52 @@ def __init__(self, program): :param program: the OpenCL program as generated by PyOpenCL """ self._program = program + self._kernels = {} for kernel in program.all_kernels(): - self.__setattr__(kernel.function_name, kernel) + self._kernels[kernel.function_name] = kernel + + def __repr__(self): + return ( + self.__class__.__name__ + + " with kernels: " + + ", ".join(self._kernels.keys()) + + "." + ) + + def __getattr__(self, name: str): + """Retrieve a kernel, if available + + :param name: name of the kernel + :return: the kernel (not a copy) + """ + if name in self._kernels: + return self._kernels[name] + raise AttributeError(f"`{self.__class__.__name__}` has no attribute `{name}`") def get_kernels(self): "return the dictionary with all kernels" - return dict( - item for item in self.__dict__.items() if not item[0].startswith("_") - ) + return self._kernels - def get_kernel(self, name): + def get_kernel(self, name: str): "get a kernel from its name" logger.debug("KernelContainer.get_kernel(%s)", name) - return self.__dict__.get(name) + return self._kernels.get(name) - def max_workgroup_size(self, kernel_name): + def max_workgroup_size(self, kernel_name: str) -> int: "Retrieve the compile time WORK_GROUP_SIZE for a given kernel" if isinstance(kernel_name, pyopencl.Kernel): kernel = kernel_name else: - kernel = self.get_kernel(kernel_name) + kernel = self._kernels.get(kernel_name) return query_kernel_info(self._program, kernel, "WORK_GROUP_SIZE") - def min_workgroup_size(self, kernel_name): + def min_workgroup_size(self, kernel_name) -> int: "Retrieve the compile time PREFERRED_WORK_GROUP_SIZE_MULTIPLE for a given kernel" if isinstance(kernel_name, pyopencl.Kernel): kernel = kernel_name else: - kernel = self.get_kernel(kernel_name) + kernel = self._kernels.get(kernel_name) return query_kernel_info( self._program, kernel, "PREFERRED_WORK_GROUP_SIZE_MULTIPLE"