fix(core): validate program-cache max_size_bytes and reject an empty cache path - #2553
Open
LeSingh1 wants to merge 1 commit into
Open
fix(core): validate program-cache max_size_bytes and reject an empty cache path#2553LeSingh1 wants to merge 1 commit into
LeSingh1 wants to merge 1 commit into
Conversation
Two argument checks in the program caches accept values that make the cache
silently useless.
max_size_bytes
--------------
Both backends guard with a bare comparison:
if max_size_bytes is not None and max_size_bytes <= 0:
raise ValueError("max_size_bytes must be positive or None ...")
bool is an int subclass, so `True` passes as a one-byte cap. Every write is
then evicted immediately by the size enforcer:
cache = InMemoryProgramCache(max_size_bytes=True)
cache[b"k"] = b"ab"
len(cache) -> 0
while its twin `False` is rejected by the same line. A float cap is accepted
too, and a str cap raises `TypeError: '<=' not supported between instances of
'str' and 'int'` rather than the ValueError the constructor documents.
Require a positive int, using the same `isinstance(x, bool) or not
isinstance(x, int)` shape already used by `Host.__new__` and
`checkpoint._check_pid`. The message keeps the word "positive" that the
existing tests match on.
path
----
`FileStreamProgramCache.__init__` does:
self._root = Path(path) if path is not None else _default_cache_dir()
`Path("")` is `Path(".")`, so an empty path roots the cache in the current
working directory and `__init__` then creates `entries/` and `tmp/` there --
directories a later `clear()` will rmdir. `path=os.environ.get("VAR", "")` is
how a caller lands on it.
The asymmetry is in the same file: `_default_cache_dir()` deliberately treats
an empty `XDG_CACHE_HOME` / `LOCALAPPDATA` as unset. Reject the empty string
rather than defaulting silently, and point at `None` for the default. An
explicit `"."` still works.
Contributor
This was referenced Aug 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Two argument checks in the program caches accept values that make the cache silently useless.
max_size_bytesBoth backends (
_in_memory.py,_file_stream.py) guard with a bare comparison:boolis anintsubclass, soTruesails through as a one-byte cap. The size enforcer then evicts every write the moment it lands:…while its twin
Falseis rejected by that very line. The comment on the guard shows0was reasoned about carefully;boolwas not.Measured on
main, both backends behave identically:max_size_bytesmain100,None0,-1ValueErrorValueErrorFalseValueErrorValueErrorTrueValueError1.5ValueError"100"TypeError: '<=' not supported between instances of 'str' and 'int'ValueErrorpathPath("")isPath("."). So an empty path roots the cache in the current working directory, and__init__immediately createsentries/andtmp/there — directories a laterclear()willrmdir. Verified:FileStreamProgramCache("")from a scratch cwd leaves['entries', 'tmp']behind.path=os.environ.get("MY_CACHE_DIR", "")is how a caller lands on it — and the asymmetry is in the same file:_default_cache_dir()deliberately treats an emptyXDG_CACHE_HOME/LOCALAPPDATAas unset:Confirmed:
XDG_CACHE_HOME=""correctly falls back to~/.cache/cuda-python/program-cache. Empty means "unset" for the env var and "cwd" for the parameter.Fix
max_size_bytes: require a positiveint, using the sameisinstance(x, bool) or not isinstance(x, int)shape already used byHost.__new__andcheckpoint._check_pid. The message keeps the wordpositive, which the existing tests match on, and now echoes the offending value.path: reject the empty string with a message pointing atNonefor the default, rather than silently defaulting (per the repo's "no success-shaped fallbacks" guidance). An explicit"."still works, andpath=Noneis untouched.Tests
Added next to the existing cap tests in
cuda_core/tests/test_program_cache.py:test_{filestream,inmemory}_cache_rejects_non_int_size_capoverTrue,1.5,"100";test_filestream_cache_rejects_an_empty_path, which also asserts nothing was created in the cwd;test_filestream_cache_still_accepts_an_explicit_dot, so the fix does not over-reach.The existing
[-1, 0]parametrization and thepath=Nonedefault-directory tests are untouched.Verification I could and could not do
_in_memory.py/_file_stream.pyimport only stdlib pluscuda.core._module.ObjectCode, so I loaded them by path with a three-line stub for that one symbol and ran every new case against bothupstream/mainand the fix:ruff check/ruff format --checkandpython -m py_compileclean on all changed files.pytest cuda_core/tests/test_program_cache.pyitself — it importscuda.core.utils, andcuda.coreis not importable here (no CUDA driver, no built extension modules). The stub run above exercises the same constructor code paths the new tests do. Please treat CI as the first real run.