From 82c0384e125ba669010e7201da2dcdef1f6a1857 Mon Sep 17 00:00:00 2001 From: payno Date: Fri, 16 Jan 2026 14:01:07 +0100 Subject: [PATCH 1/3] pyopencl.cache._create_built_program_from_source_cached: fix typo in string operation. --- pyopencl/cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyopencl/cache.py b/pyopencl/cache.py index a9c5d0b42..e758f72b4 100644 --- a/pyopencl/cache.py +++ b/pyopencl/cache.py @@ -422,7 +422,7 @@ def _create_built_program_from_source_cached( # defeat implementation caches: from uuid import uuid4 src = src + b"\n\n__constant int pyopencl_defeat_cache_%s = 0;" % ( - uuid4().hex) + uuid4().hex.encode()) logger.debug( "build program: start building program from source on %s", From 0015a182675175c0497b7b33573f941809cdb49e Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 16 Jan 2026 13:34:00 -0600 Subject: [PATCH 2/3] Ensure cache code path failures are fatal in CI --- .github/workflows/ci.yml | 2 ++ .gitlab-ci.yml | 2 ++ pyopencl/cache.py | 15 +++++++++------ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2f14580f..cf71bcc92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,7 @@ jobs: - uses: actions/checkout@v6 - name: "Main Script" run: | + export PYOPENCL_CACHE_FAILURE_FATAL=1 export CONDA_ENVIRONMENT=.test-conda-env-py3.yml .ci/hack-intel-cl-into-conda-env.sh @@ -86,6 +87,7 @@ jobs: shell: bash run: | set -x + export PYOPENCL_CACHE_FAILURE_FATAL=1 export CONDA_ENVIRONMENT=.test-conda-env-py3.yml sed -i 's/- ocl-icd/- khronos-opencl-icd-loader/g' "$CONDA_ENVIRONMENT" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e6759e76a..246236502 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,6 +4,7 @@ variables: Python 3 Intel CPU: script: | source /opt/enable-intel-cl.sh + export PYOPENCL_CACHE_FAILURE_FATAL=1 export PYOPENCL_TEST="intel(r):pu" export EXTRA_INSTALL="numpy mako" @@ -22,6 +23,7 @@ Python 3 Intel CPU: Python 3 Nvidia Titan V: script: | + export PYOPENCL_CACHE_FAILURE_FATAL=1 export PYOPENCL_TEST=nvi:titan export EXTRA_INSTALL="numpy mako" diff --git a/pyopencl/cache.py b/pyopencl/cache.py index e758f72b4..fb42ff689 100644 --- a/pyopencl/cache.py +++ b/pyopencl/cache.py @@ -532,12 +532,15 @@ def create_built_program_from_source_cached( raise if not build_program_failure: - from traceback import format_exc - from warnings import warn - warn( - "PyOpenCL compiler caching failed with an exception:\n" - f"[begin exception]\n{format_exc()}[end exception]", - stacklevel=2) + if os.environ["PYOPENCL_CACHE_FAILURE_FATAL"]: + raise + else: + from traceback import format_exc + from warnings import warn + warn( + "PyOpenCL compiler caching failed with an exception:\n" + f"[begin exception]\n{format_exc()}[end exception]", + stacklevel=2) prg = _cl._Program(ctx, src) was_cached = False From 3a4678c9faf177870610f38f485e98af8d1732b5 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 16 Jan 2026 13:45:20 -0600 Subject: [PATCH 3/3] Fix a few more crashes/type issues in compiler caching --- pyopencl/cache.py | 27 +++++++++++++++------------ src/wrap_cl_part_2.cpp | 8 ++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/pyopencl/cache.py b/pyopencl/cache.py index fb42ff689..152b35043 100644 --- a/pyopencl/cache.py +++ b/pyopencl/cache.py @@ -169,26 +169,26 @@ def error_clean_up(self): # {{{ #include dependency handling -C_INCLUDE_RE = re.compile(r'^\s*\#\s*include\s+[<"](.+)[">]\s*$', +C_INCLUDE_RE = re.compile(rb'^\s*\#\s*include\s+[<"](.+)[">]\s*$', re.MULTILINE) -def get_dependencies(src, include_path): - result = {} +def get_dependencies(src: bytes, include_path: Sequence[str]): + result: dict[str, tuple[float, str] | None] = {} from os.path import join, realpath - def _inner(src): + def _inner(src: bytes): for match in C_INCLUDE_RE.finditer(src): included = match.group(1) found = False for ipath in include_path: - included_file_name = realpath(join(ipath, included)) + included_file_name = realpath(join(ipath, included.decode())) if included_file_name not in result: try: - src_file = open(included_file_name) + src_file = open(included_file_name, "rb") except OSError: continue @@ -218,13 +218,16 @@ def _inner(src): _inner(src) - result = [(name, *vals) for name, vals in result.items()] - result.sort() + result_list = [ + (name, *vals) for name, vals in result.items() + if vals is not None + ] + result_list.sort() - return result + return result_list -def get_file_md5sum(fname): +def get_file_md5sum(fname: str): checksum = new_hash() inf = open(fname) try: @@ -342,7 +345,7 @@ class _InvalidInfoFileError(RuntimeError): @dataclass(frozen=True) class _SourceInfo: - dependencies: list[tuple[str, ...]] + dependencies: list[tuple[str, float, str]] log: str | None @@ -483,7 +486,7 @@ def _create_built_program_from_source_cached( from pickle import dump info_file = open(info_path, "wb") dump(_SourceInfo( - dependencies=get_dependencies(src, include_path), + dependencies=get_dependencies(src, include_path or []), log=logs[i]), info_file) info_file.close() diff --git a/src/wrap_cl_part_2.cpp b/src/wrap_cl_part_2.cpp index 03158afdd..7d8c71fe0 100644 --- a/src/wrap_cl_part_2.cpp +++ b/src/wrap_cl_part_2.cpp @@ -501,6 +501,14 @@ void pyopencl_expose_part_2(py::module_ &m) }, py::arg("context"), py::arg("src")) + .def( + "__init__", + [](cls *self, context &ctx, py::bytes const &src) + { + create_program_with_source(self, ctx, std::string((const char *) src.data(), src.size())); + }, + py::arg("context"), + py::arg("src")) .def( "__init__", [](cls *self, context &ctx, py::sequence devices, py::sequence binaries)