Repository navigation
[cache] @cache raises TypeError for unhashable arguments (e.g. Pydantic model inputs) - #110
Conversation
ROHIT-JR
left a comment
There was a problem hiding this comment.
Thanks for this. The approach matches issue #59, CI is green on 3.10, 3.11 and 3.12, and the new tests pass on my checkout. I ran the code with extra cases, and two of them can return a wrong cached result. That is worse than the TypeError this PR removes, so I'm asking for changes.
Changes needed
1. A field marked exclude=True is left out of the cache key, so different inputs share one entry.
model_dump(mode="json") drops excluded fields.
class E(BaseModel):
q: str
secret: str = Field(default="", exclude=True)
@cache(ttl=60)
async def g(x): return x.secret
await g(E(q="a", secret="alice")) # 'alice'
await g(E(q="a", secret="bob")) # 'alice' <- should be 'bob'If such a field is something like a user id or tenant, one caller gets another caller's cached result. Please build the key from all model fields. Checked on this checkout: pydantic_core.to_jsonable_python({n: getattr(v, n) for n in type(v).model_fields}) includes the excluded field.
2. The key uses only type(value).__name__, so two different models with the same class name collide.
Two Q models from different modules (or defined inside different functions) share a key. In my run, f(B(q="a")) returned the result cached for A(q="a"). Please use type(value).__module__ and __qualname__, or the class object itself.
Please add a regression test for each. Both fail on the current code.
Non-blocking
- A custom
key=that returns something unhashable (for example a list) fails with the rawcannot use 'list' as a dict keymessage. The same descriptive error would help here. - A model with an arbitrary type raises
PydanticSerializationErrorrather than the descriptiveTypeErrorthat points tokey=. - A list of models, a very common input, still raises
TypeError(a tuple of models does too). The message points tokey=, so this is acceptable, but a short note in the docstring would help. - Tidy-up:
cache_lookup_keyis just an alias ofcache_key, so one name is enough.
Everything else I checked works: a parameter named key on the cached function is fine, and ExecutionContext arguments are still skipped.
|
Hi @ROHIT-JR , thanks for the catch, I have addressed these issues and added a few updated test cases for it |
@cacheraised an unhandledTypeErrorwhen passed unhashable arguments (e.g. Pydantic models).Changes:
BaseModelarguments are now serialized viamodel_dump(mode="json")for a stable cache key.key=callable tocache()for custom key logic on any argument type.TypeErrornaming the offending argument.keyparameter.tests/test_cache_unhashable.pycovering all of the above.Closes #59