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

Add support for decorating async functions with pn.io.cache #5649

Merged
merged 3 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
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
42 changes: 32 additions & 10 deletions panel/io/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import param

from param.parameterized import iscoroutinefunction

from .state import state

#---------------------------------------------------------------------
Expand Down Expand Up @@ -351,8 +353,7 @@ def cache(

lock = threading.RLock()

@functools.wraps(func)
def wrapped_func(*args, **kwargs):
def hash_func(*args, **kwargs):
global func_hash
# Handle param.depends method by adding parameters to arguments
func_name = func.__name__
Expand Down Expand Up @@ -396,18 +397,39 @@ def wrapped_func(*args, **kwargs):
_cleanup_ttl(func_cache, ttl, time)

if hash_value in func_cache:
with lock:
ret, ts, count, _ = func_cache[hash_value]
func_cache[hash_value] = (ret, ts, count+1, time)
return ret
return func_cache, hash_value, time

if max_items is not None:
_cleanup_cache(func_cache, policy, max_items, time)

ret = func(*args, **kwargs)
with lock:
func_cache[hash_value] = (ret, time, 0, time)
return ret
return func_cache, hash_value, time

if iscoroutinefunction(func):
@functools.wraps(func)
async def wrapped_func(*args, **kwargs):
func_cache, hash_value, time = hash_func(*args, **kwargs)
if hash_value in func_cache:
with lock:
ret, ts, count, _ = func_cache[hash_value]
func_cache[hash_value] = (ret, ts, count+1, time)
else:
ret = await func(*args, **kwargs)
with lock:
func_cache[hash_value] = (ret, time, 0, time)
return ret
else:
@functools.wraps(func)
def wrapped_func(*args, **kwargs):
func_cache, hash_value, time = hash_func(*args, **kwargs)
if hash_value in func_cache:
with lock:
ret, ts, count, _ = func_cache[hash_value]
func_cache[hash_value] = (ret, ts, count+1, time)
else:
ret = func(*args, **kwargs)
with lock:
func_cache[hash_value] = (ret, time, 0, time)
return ret

def clear(session_context=None):
global func_hash
Expand Down
14 changes: 14 additions & 0 deletions panel/tests/io/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,27 @@ def function_with_args(a, b):
OFFSET[(a, b)] = offset + 1
return result

async def async_function_with_args(a, b):
global OFFSET
offset = OFFSET.get((a, b), 0)
result = a + b + offset
OFFSET[(a, b)] = offset + 1
return result

def test_cache_with_args():
global OFFSET
OFFSET.clear()
fn = cache(function_with_args)
assert fn(0, 0) == 0
assert fn(0, 0) == 0

async def test_async_cache_with_args():
global OFFSET
OFFSET.clear()
fn = cache(async_function_with_args)
assert (await fn(0, 0)) == 0
assert (await fn(0, 0)) == 0

def test_cache_with_kwargs():
global OFFSET
OFFSET.clear()
Expand Down
Loading