Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure hash_funcs are applied recursively in cache #4334

Merged
merged 1 commit into from
Jan 26, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions panel/io/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import unittest.mock
import weakref

from contextlib import contextmanager

import param

from .state import state
Expand Down Expand Up @@ -154,11 +156,9 @@ def _io_hash(obj):
for name in _FFI_TYPE_NAMES:
_hash_funcs[name] = b'0'

def _find_hash_func(obj, hash_funcs={}):
def _find_hash_func(obj):
fqn_type = _get_fqn(obj)
if fqn_type in hash_funcs:
return hash_funcs[fqn_type]
elif fqn_type in _hash_funcs:
if fqn_type in _hash_funcs:
return _hash_funcs[fqn_type]
for otype, hash_func in _hash_funcs.items():
if isinstance(otype, str):
Expand All @@ -170,8 +170,8 @@ def _find_hash_func(obj, hash_funcs={}):
elif isinstance(obj, otype):
return hash_func

def _generate_hash_inner(obj, hash_funcs={}):
hash_func = _find_hash_func(obj, hash_funcs)
def _generate_hash_inner(obj):
hash_func = _find_hash_func(obj)
if hash_func is not None:
try:
output = hash_func(obj)
Expand All @@ -192,14 +192,14 @@ def _generate_hash_inner(obj, hash_funcs={}):
return h.digest()
return _int_to_bytes(id(obj))

def _generate_hash(obj, hash_funcs={}):
def _generate_hash(obj):
# Break recursive cycles.
hash_stack = state._current_stack
if obj in hash_stack:
return _CYCLE_PLACEHOLDER
hash_stack.push(obj)
try:
hash_value = _generate_hash_inner(obj, hash_funcs)
hash_value = _generate_hash_inner(obj)
finally:
hash_stack.pop()
return hash_value
Expand Down Expand Up @@ -245,6 +245,16 @@ def _cleanup_ttl(cache, ttl, time):
if (time-ts) > ttl:
del cache[key]

@contextmanager
def _override_hash_funcs(hash_funcs):
backup = dict(_hash_funcs)
_hash_funcs.update(hash_funcs)
try:
yield
finally:
_hash_funcs.clear()
_hash_funcs.update(backup)

#---------------------------------------------------------------------
# Public API
#---------------------------------------------------------------------
Expand All @@ -268,10 +278,11 @@ def compute_hash(func, hash_funcs, args, kwargs):
if _INDETERMINATE not in key and key in _HASH_MAP:
return _HASH_MAP[key]
hasher = hashlib.new("md5")
if args:
hasher.update(_generate_hash(args, hash_funcs))
if kwargs:
hasher.update(_generate_hash(kwargs, hash_funcs))
with _override_hash_funcs(hash_funcs):
if args:
hasher.update(_generate_hash(args))
if kwargs:
hasher.update(_generate_hash(kwargs))
hash_value = hasher.hexdigest()
if _INDETERMINATE not in key:
_HASH_MAP[key] = hash_value
Expand Down