Skip to content

Commit 4fffc19

Browse files
cpcloudclaude
andcommitted
Fix ruff check, format, and SPDX headers for program cache
- SPDX: LicenseRef-NVIDIA-SOFTWARE-LICENSE -> Apache-2.0 in tests - B027: noqa for intentional no-op close() - S301: noqa for pickle.loads (fundamental to cache design) - B904: add 'from None' to re-raised KeyErrors - SIM105: replace try-except-pass with contextlib.suppress - ruff format applied Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f7a488d commit 4fffc19

File tree

3 files changed

+57
-116
lines changed

3 files changed

+57
-116
lines changed

cuda_core/cuda/core/utils/_program_cache.py

Lines changed: 34 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,24 @@
1818
from __future__ import annotations
1919

2020
import abc
21+
import contextlib
2122
import hashlib
2223
import os
2324
import pickle
2425
import tempfile
2526
import time
2627
from pathlib import Path
27-
from typing import Hashable, Iterable, Optional, Sequence
28+
from typing import Hashable, Iterable, Sequence
2829

2930
from cuda.core._module import ObjectCode
3031
from cuda.core._program import ProgramOptions
3132
from cuda.core._utils.cuda_utils import (
3233
driver as _driver,
34+
)
35+
from cuda.core._utils.cuda_utils import (
3336
handle_return as _handle_return,
37+
)
38+
from cuda.core._utils.cuda_utils import (
3439
nvrtc as _nvrtc,
3540
)
3641

@@ -47,9 +52,7 @@
4752

4853
def _require_object_code(value: object) -> ObjectCode:
4954
if not isinstance(value, ObjectCode):
50-
raise TypeError(
51-
f"cache values must be ObjectCode instances, got {type(value).__name__}"
52-
)
55+
raise TypeError(f"cache values must be ObjectCode instances, got {type(value).__name__}")
5356
return value
5457

5558

@@ -131,10 +134,10 @@ def get(self, key: Hashable, default: ObjectCode | None = None) -> ObjectCode |
131134
except KeyError:
132135
return default
133136

134-
def close(self) -> None:
137+
def close(self) -> None: # noqa: B027
135138
"""Release backend resources. No-op by default."""
136139

137-
def __enter__(self) -> "ProgramCacheResource":
140+
def __enter__(self) -> ProgramCacheResource:
138141
return self
139142

140143
def __exit__(self, exc_type, exc_value, traceback) -> None:
@@ -214,24 +217,16 @@ def make_program_cache_key(
214217
A 32-byte blake2b digest suitable for use as a cache key.
215218
"""
216219
if code_type not in _VALID_CODE_TYPES:
217-
raise ValueError(
218-
f"code_type={code_type!r} is not supported "
219-
f"(must be one of {sorted(_VALID_CODE_TYPES)})"
220-
)
220+
raise ValueError(f"code_type={code_type!r} is not supported (must be one of {sorted(_VALID_CODE_TYPES)})")
221221
if target_type not in _VALID_TARGET_TYPES:
222-
raise ValueError(
223-
f"target_type={target_type!r} is not supported "
224-
f"(must be one of {sorted(_VALID_TARGET_TYPES)})"
225-
)
222+
raise ValueError(f"target_type={target_type!r} is not supported (must be one of {sorted(_VALID_TARGET_TYPES)})")
226223

227224
if isinstance(code, str):
228225
code_bytes = code.encode("utf-8")
229226
elif isinstance(code, (bytes, bytearray)):
230227
code_bytes = bytes(code)
231228
else:
232-
raise TypeError(
233-
f"code must be str or bytes, got {type(code).__name__}"
234-
)
229+
raise TypeError(f"code must be str or bytes, got {type(code).__name__}")
235230

236231
backend = _backend_for_code_type(code_type)
237232
# ProgramOptions.as_bytes may or may not accept target_type depending on
@@ -328,7 +323,7 @@ def __init__(
328323
self,
329324
path: str | os.PathLike,
330325
*,
331-
max_size_bytes: Optional[int] = None,
326+
max_size_bytes: int | None = None,
332327
) -> None:
333328
if max_size_bytes is not None and max_size_bytes < 0:
334329
raise ValueError("max_size_bytes must be non-negative or None")
@@ -338,7 +333,7 @@ def __init__(
338333
import sqlite3
339334

340335
self._sqlite3 = sqlite3
341-
self._conn: Optional[sqlite3.Connection] = None
336+
self._conn: sqlite3.Connection | None = None
342337
self._open()
343338

344339
# -- lifecycle -----------------------------------------------------------
@@ -396,29 +391,25 @@ def _require_open(self):
396391

397392
def __contains__(self, key: object) -> bool:
398393
k = _as_key_bytes(key)
399-
row = self._require_open().execute(
400-
"SELECT 1 FROM entries WHERE key = ?", (k,)
401-
).fetchone()
394+
row = self._require_open().execute("SELECT 1 FROM entries WHERE key = ?", (k,)).fetchone()
402395
return row is not None
403396

404397
def __getitem__(self, key: object) -> ObjectCode:
405398
k = _as_key_bytes(key)
406399
conn = self._require_open()
407-
row = conn.execute(
408-
"SELECT payload FROM entries WHERE key = ?", (k,)
409-
).fetchone()
400+
row = conn.execute("SELECT payload FROM entries WHERE key = ?", (k,)).fetchone()
410401
if row is None:
411402
raise KeyError(key)
412403
payload = row[0]
413404
try:
414-
value = pickle.loads(payload)
405+
value = pickle.loads(payload) # noqa: S301
415406
except Exception:
416407
# Corrupt entry -- delete and treat as a miss.
417408
conn.execute("DELETE FROM entries WHERE key = ?", (k,))
418-
raise KeyError(key)
409+
raise KeyError(key) from None
419410
if not isinstance(value, ObjectCode):
420411
conn.execute("DELETE FROM entries WHERE key = ?", (k,))
421-
raise KeyError(key)
412+
raise KeyError(key) from None
422413
conn.execute(
423414
"UPDATE entries SET accessed_at = ? WHERE key = ?",
424415
(time.time(), k),
@@ -452,9 +443,7 @@ def __delitem__(self, key: object) -> None:
452443
raise KeyError(key)
453444

454445
def __len__(self) -> int:
455-
(n,) = self._require_open().execute(
456-
"SELECT COUNT(*) FROM entries"
457-
).fetchone()
446+
(n,) = self._require_open().execute("SELECT COUNT(*) FROM entries").fetchone()
458447
return int(n)
459448

460449
def clear(self) -> None:
@@ -466,9 +455,7 @@ def _enforce_size_cap(self) -> None:
466455
if self._max_size_bytes is None:
467456
return
468457
conn = self._require_open()
469-
(total,) = conn.execute(
470-
"SELECT COALESCE(SUM(size_bytes), 0) FROM entries"
471-
).fetchone()
458+
(total,) = conn.execute("SELECT COALESCE(SUM(size_bytes), 0) FROM entries").fetchone()
472459
if total <= self._max_size_bytes:
473460
return
474461
# Delete oldest (least-recently-used) until at or under the cap.
@@ -514,7 +501,7 @@ def __init__(
514501
self,
515502
path: str | os.PathLike,
516503
*,
517-
max_size_bytes: Optional[int] = None,
504+
max_size_bytes: int | None = None,
518505
) -> None:
519506
if max_size_bytes is not None and max_size_bytes < 0:
520507
raise ValueError("max_size_bytes must be non-negative or None")
@@ -548,29 +535,25 @@ def __getitem__(self, key: object) -> ObjectCode:
548535
try:
549536
data = path.read_bytes()
550537
except FileNotFoundError:
551-
raise KeyError(key)
538+
raise KeyError(key) from None
552539
k = _as_key_bytes(key)
553540
try:
554-
record = pickle.loads(data)
541+
record = pickle.loads(data) # noqa: S301
555542
schema, stored_key, payload, _created_at = record
556543
if schema != _FILESTREAM_SCHEMA_VERSION:
557544
raise ValueError(f"unknown schema {schema}")
558545
if stored_key != k:
559546
raise ValueError("key mismatch")
560-
value = pickle.loads(payload)
547+
value = pickle.loads(payload) # noqa: S301
561548
except Exception:
562549
# Corrupt entry -- delete and treat as a miss.
563-
try:
550+
with contextlib.suppress(FileNotFoundError):
564551
path.unlink()
565-
except FileNotFoundError:
566-
pass
567-
raise KeyError(key)
552+
raise KeyError(key) from None
568553
if not isinstance(value, ObjectCode):
569-
try:
554+
with contextlib.suppress(FileNotFoundError):
570555
path.unlink()
571-
except FileNotFoundError:
572-
pass
573-
raise KeyError(key)
556+
raise KeyError(key) from None
574557
return value
575558

576559
def __setitem__(self, key: object, value: object) -> None:
@@ -594,10 +577,8 @@ def __setitem__(self, key: object, value: object) -> None:
594577
os.fsync(fh.fileno())
595578
os.replace(tmp_path, target)
596579
except BaseException:
597-
try:
580+
with contextlib.suppress(FileNotFoundError):
598581
tmp_path.unlink()
599-
except FileNotFoundError:
600-
pass
601582
raise
602583
self._enforce_size_cap()
603584

@@ -606,7 +587,7 @@ def __delitem__(self, key: object) -> None:
606587
try:
607588
path.unlink()
608589
except FileNotFoundError:
609-
raise KeyError(key)
590+
raise KeyError(key) from None
610591

611592
def __len__(self) -> int:
612593
count = 0
@@ -616,18 +597,14 @@ def __len__(self) -> int:
616597

617598
def clear(self) -> None:
618599
for path in list(self._iter_entry_paths()):
619-
try:
600+
with contextlib.suppress(FileNotFoundError):
620601
path.unlink()
621-
except FileNotFoundError:
622-
pass
623602
# Remove empty subdirs (best-effort; concurrent writers may re-create).
624603
if self._entries.exists():
625604
for sub in sorted(self._entries.iterdir(), reverse=True):
626605
if sub.is_dir():
627-
try:
606+
with contextlib.suppress(OSError):
628607
sub.rmdir()
629-
except OSError:
630-
pass
631608

632609
# -- internals -----------------------------------------------------------
633610

@@ -659,8 +636,6 @@ def _enforce_size_cap(self) -> None:
659636
for _mtime, size, path in entries:
660637
if total <= self._max_size_bytes:
661638
return
662-
try:
639+
with contextlib.suppress(FileNotFoundError):
663640
path.unlink()
664641
total -= size
665-
except FileNotFoundError:
666-
pass

cuda_core/tests/test_program_cache.py

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
3-
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
3+
# SPDX-License-Identifier: Apache-2.0
44

55
import abc
66
import time
@@ -131,48 +131,32 @@ def test_make_program_cache_key_returns_bytes():
131131
def test_make_program_cache_key_is_deterministic():
132132
from cuda.core.utils import make_program_cache_key
133133

134-
k1 = make_program_cache_key(
135-
code="void k(){}", code_type="c++", options=_opts(), target_type="cubin"
136-
)
137-
k2 = make_program_cache_key(
138-
code="void k(){}", code_type="c++", options=_opts(), target_type="cubin"
139-
)
134+
k1 = make_program_cache_key(code="void k(){}", code_type="c++", options=_opts(), target_type="cubin")
135+
k2 = make_program_cache_key(code="void k(){}", code_type="c++", options=_opts(), target_type="cubin")
140136
assert k1 == k2
141137

142138

143139
def test_make_program_cache_key_changes_with_code():
144140
from cuda.core.utils import make_program_cache_key
145141

146-
k1 = make_program_cache_key(
147-
code="a", code_type="c++", options=_opts(), target_type="cubin"
148-
)
149-
k2 = make_program_cache_key(
150-
code="b", code_type="c++", options=_opts(), target_type="cubin"
151-
)
142+
k1 = make_program_cache_key(code="a", code_type="c++", options=_opts(), target_type="cubin")
143+
k2 = make_program_cache_key(code="b", code_type="c++", options=_opts(), target_type="cubin")
152144
assert k1 != k2
153145

154146

155147
def test_make_program_cache_key_changes_with_target_type():
156148
from cuda.core.utils import make_program_cache_key
157149

158-
k1 = make_program_cache_key(
159-
code="a", code_type="c++", options=_opts(), target_type="ptx"
160-
)
161-
k2 = make_program_cache_key(
162-
code="a", code_type="c++", options=_opts(), target_type="cubin"
163-
)
150+
k1 = make_program_cache_key(code="a", code_type="c++", options=_opts(), target_type="ptx")
151+
k2 = make_program_cache_key(code="a", code_type="c++", options=_opts(), target_type="cubin")
164152
assert k1 != k2
165153

166154

167155
def test_make_program_cache_key_changes_with_arch():
168156
from cuda.core.utils import make_program_cache_key
169157

170-
k1 = make_program_cache_key(
171-
code="a", code_type="c++", options=_opts(arch="sm_80"), target_type="cubin"
172-
)
173-
k2 = make_program_cache_key(
174-
code="a", code_type="c++", options=_opts(arch="sm_90"), target_type="cubin"
175-
)
158+
k1 = make_program_cache_key(code="a", code_type="c++", options=_opts(arch="sm_80"), target_type="cubin")
159+
k2 = make_program_cache_key(code="a", code_type="c++", options=_opts(arch="sm_90"), target_type="cubin")
176160
assert k1 != k2
177161

178162

@@ -218,40 +202,30 @@ def test_make_program_cache_key_accepts_bytes_code():
218202
from cuda.core.utils import make_program_cache_key
219203

220204
# NVVM IR is bytes; accept both str and bytes equivalently (str is UTF-8).
221-
k_str = make_program_cache_key(
222-
code="abc", code_type="nvvm", options=_opts(), target_type="ptx"
223-
)
224-
k_bytes = make_program_cache_key(
225-
code=b"abc", code_type="nvvm", options=_opts(), target_type="ptx"
226-
)
205+
k_str = make_program_cache_key(code="abc", code_type="nvvm", options=_opts(), target_type="ptx")
206+
k_bytes = make_program_cache_key(code=b"abc", code_type="nvvm", options=_opts(), target_type="ptx")
227207
assert k_str == k_bytes
228208

229209

230210
def test_make_program_cache_key_rejects_unknown_code_type():
231211
from cuda.core.utils import make_program_cache_key
232212

233213
with pytest.raises(ValueError, match="code_type"):
234-
make_program_cache_key(
235-
code="a", code_type="fortran", options=_opts(), target_type="cubin"
236-
)
214+
make_program_cache_key(code="a", code_type="fortran", options=_opts(), target_type="cubin")
237215

238216

239217
def test_make_program_cache_key_rejects_unknown_target_type():
240218
from cuda.core.utils import make_program_cache_key
241219

242220
with pytest.raises(ValueError, match="target_type"):
243-
make_program_cache_key(
244-
code="a", code_type="c++", options=_opts(), target_type="exe"
245-
)
221+
make_program_cache_key(code="a", code_type="c++", options=_opts(), target_type="exe")
246222

247223

248224
def test_make_program_cache_key_rejects_non_str_bytes_code():
249225
from cuda.core.utils import make_program_cache_key
250226

251227
with pytest.raises(TypeError, match="code"):
252-
make_program_cache_key(
253-
code=12345, code_type="c++", options=_opts(), target_type="cubin"
254-
)
228+
make_program_cache_key(code=12345, code_type="c++", options=_opts(), target_type="cubin")
255229

256230

257231
# ---------------------------------------------------------------------------
@@ -363,9 +337,8 @@ def test_sqlite_cache_corruption_is_reported_as_miss(tmp_path):
363337
def test_sqlite_cache_rejects_non_object_code(tmp_path):
364338
from cuda.core.utils import SQLiteProgramCache
365339

366-
with SQLiteProgramCache(tmp_path / "cache.db") as cache:
367-
with pytest.raises(TypeError, match="ObjectCode"):
368-
cache[b"k"] = b"not an ObjectCode"
340+
with SQLiteProgramCache(tmp_path / "cache.db") as cache, pytest.raises(TypeError, match="ObjectCode"):
341+
cache[b"k"] = b"not an ObjectCode"
369342

370343

371344
def test_sqlite_cache_accepts_str_keys(tmp_path):
@@ -511,6 +484,7 @@ def test_filestream_cache_atomic_no_half_written_file(tmp_path, monkeypatch):
511484
from cuda.core.utils import FileStreamProgramCache
512485

513486
with FileStreamProgramCache(tmp_path / "fc") as cache:
487+
514488
def _boom(src, dst):
515489
raise RuntimeError("crash during replace")
516490

@@ -540,9 +514,8 @@ def test_filestream_cache_corruption_is_reported_as_miss(tmp_path):
540514
def test_filestream_cache_rejects_non_object_code(tmp_path):
541515
from cuda.core.utils import FileStreamProgramCache
542516

543-
with FileStreamProgramCache(tmp_path / "fc") as cache:
544-
with pytest.raises(TypeError, match="ObjectCode"):
545-
cache[b"k"] = b"not an ObjectCode"
517+
with FileStreamProgramCache(tmp_path / "fc") as cache, pytest.raises(TypeError, match="ObjectCode"):
518+
cache[b"k"] = b"not an ObjectCode"
546519

547520

548521
def test_filestream_cache_rejects_negative_size_cap(tmp_path):

0 commit comments

Comments
 (0)