Fix cache_key collapse of non-pydantic private/__main__ objects - #259
Draft
ptomecek wants to merge 1 commit into
Draft
Fix cache_key collapse of non-pydantic private/__main__ objects#259ptomecek wants to merge 1 commit into
ptomecek wants to merge 1 commit into
Conversation
normalize_token's fallback name-only-tokenized any object whose type module started with "_" or contained "._", silently dropping instance state. Distinct values (e.g. callables authored in __main__ or a private module) therefore collapsed onto one cache key, causing false cache hits and stale results toward the dangerous direction. Make serializability the primary signal instead of the module name: any object cloudpickle can serialize now folds its state into the token, so genuinely-different values stay distinct. The module-name heuristic is consulted only when serialization fails, to give behavior-irrelevant interpreter internals (e.g. _abc._abc_data exposed in ABC-derived class closures on Python 3.14) a stable name-only token instead of crashing behavior hashing, while opaque user objects fail loud rather than silently sharing a key. A small allowlist keeps picklable-but-volatile framework internals (pydantic compiled validators) name-only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Pascal Tomecek <pascal.tomecek@cubistsystematic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #259 +/- ##
==========================================
+ Coverage 93.48% 93.49% +0.01%
==========================================
Files 176 176
Lines 20327 20377 +50
Branches 1350 1351 +1
==========================================
+ Hits 19002 19051 +49
- Misses 1052 1053 +1
Partials 273 273 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
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.
Summary
normalize_token's generic fallback inccflow/utils/tokenize.pyname-only-tokenized any object whose type module started with_or contained._, returning("__internal__", module, qualname)and silently dropping instance state. Because"__main__".startswith("_")isTrueand private submodules (pkg._internal) contain._, any non-pydantic object authored in a notebook/REPL (__main__) or a private module collapsed onto a single token.Two genuinely-different values therefore produced the same
cache_key, causing false cache hits / stale results — the dangerous direction (toward incorrect reuse rather than safe over-recompute).Note: pydantic
BaseModels, functions, methods, code objects, partials and builtins all have dedicated handlers and were never affected — this only ever hit the fallback for arbitrary unregistered objects.Fix
Make serializability the primary signal instead of the module name:
cloudpicklecan serialize now folds its state into the token, so genuinely-different values stay distinct (fixes the reported collapse)._abc._abc_data, which Python 3.14 exposes in ABC-derived classes' function closures and which raise oncloudpickle.dumps) → stable name-only token, so behavior hashing does not crash; from__main__) → fail loud withTypeErrorrather than silently sharing a key.This is version-agnostic (3.11–3.14): it keys on the serializability property, not on an enumeration of internal modules.
Why this is the right shape
Instrumenting the old branch across the full test suite showed the only objects that ever reached it were the library's own internal helpers — including a stateful frozen dataclass that was itself being name-collapsed (a latent collision the fix also resolves). The ecosystem-internal (
_abc/pydantic-C) case is forward-looking (Python 3.14). A static module allowlist would have been unmaintainable and would not even have covered the library's own modules; serializability is the durable signal.Tests
New
TestPrivateModuleNoStateCollapsecovers:_secret/pkg._internal/__main__→ distinct and deterministic tokens;__main__→ raisesTypeError(no silent collision);Validation
normalize_tokenlogic re-validated on Python 3.12.ruffclean.