Make it easier to use pn.state.as_cached by supporting unnamed arguments
When trying to cache (via as_cached) a function that you normally use with unnamed arguments its friction you have to spend time on looking up the name of the argument.
Example:
Normally I do pd.read_csv("my_file.csv") and similar. Thus i would expect to be able to do
pn.state.as_cached("data", pd.read_csv, "my_file.csv")
But this raises
TypeError: unsupported operand type(s) for +: 'float' and 'str'
Traceback (most recent call last):
File "C:\repos\private\panel\.venv\lib\site-packages\bokeh\application\handlers\code_runner.py", line 231, in run
exec(self._code, module.__dict__)
File "C:\repos\private\panel\script.py", line 6, in <module>
data = pn.state.as_cached("data", pd.read_csv, "tabulator.csv")
File "c:\repos\private\panel\panel\io\state.py", line 453, in as_cached
new_expiry = time.monotonic() + ttl if ttl else None
TypeError: unsupported operand type(s) for +: 'float' and 'str'
Thus i have to lookup the name of the argument filepath_or_buffer which I never use otherwise.
Please change to the function signature
from
def as_cached(self, key: str, fn: Callable[[], T], ttl: int = None, **kwargs) -> T:
def as_cached(self, key: str, fn: Callable[[], T], *args, ttl: int = None, **kwargs) -> T:
This will probably need a transition period. What you suggest breaks backward compatibility.
Function signature during the transition period could require ttl to be a keyword:
def as_cached(self, key: str, fn: Callable[[], T], *, ttl: int = None, **kwargs) -> T:
I would suggest making the change in connecting with 1.0. There we can fix anything that should have been different.
You are probably right @Hoxbro and I'm too hasty 👍
We really should review the API and use keyword only args where possible.
Yes. This is such a time waster. Everytime I want to use pd.read_csv and as_cached I have to look up that the argument is called filepath_or_buffer. No body writes that. No body remembers that. It just makes the code harder to read.
I was also confused by this, because according to the docs:
"If provided, the args and kwargs will also be hashed making it easy to cache (or memoize) on the arguments to the function:"
def load_data(*args, **kwargs):
return ... # Load some data
data = pn.state.as_cached('data', load_data, *args, **kwargs)
Which seems to imply that you can pass positional arguments or *args and **kwargs directly after the function, but in reality, everything has to be passed as keyword arguments (named parameters).
Also, currently fn requires a Callable[[], T] which is a function that accepts no arg/kwargs. I believe it should be updated to Callable[..., T] (ellipsis literal) to allow any arbitrary args/kwargs to be passed to the given function.
Didn't think this was worth creating a new issue, but let me know if I should.