Skip to content

Commit

Permalink
Allow async callbacks on FileDownload (#5878)
Browse files Browse the repository at this point in the history
* Allow async callbacks on FileDownload

* Add test
  • Loading branch information
philippjfr authored Nov 15, 2023
1 parent 5425c20 commit f73973d
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 14 deletions.
18 changes: 18 additions & 0 deletions panel/tests/widgets/test_misc.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import asyncio

from io import StringIO
from pathlib import Path

Expand Down Expand Up @@ -103,6 +105,22 @@ def cb():
assert file_download.label == "Download cba.py"


@pytest.mark.asyncio
async def test_file_download_async_callback():
async def cb():
return StringIO("data")

file_download = FileDownload(callback=cb, filename="abc.py")

assert file_download.data is None

file_download._clicks += 1

await asyncio.sleep(0.1)

assert file_download.data == "data:application/octet-stream;base64,ZGF0YQ=="


def test_file_download_transfers():
file_download = FileDownload(__file__, embed=True)
assert file_download._transfers == 1
Expand Down
38 changes: 24 additions & 14 deletions panel/widgets/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import param

from param.parameterized import eval_function_with_deps, iscoroutinefunction
from pyviz_comms import JupyterComm

from ..io.notebook import push
Expand Down Expand Up @@ -207,21 +208,8 @@ def _update_embed(self):
if self.embed:
self._transfer()

@param.depends('_clicks', watch=True)
def _transfer(self):
if self.file is None and self.callback is None:
if self.embed:
raise ValueError('Must provide a file or a callback '
'if it is to be embedded.')
return

from ..param import ParamFunction
if self.callback is None:
fileobj = self.file
else:
fileobj = ParamFunction.eval(self.callback)
def _sync_data(self, fileobj):
filename = self.filename

if isinstance(fileobj, (str, Path)):
fileobj = Path(fileobj)
if not fileobj.exists():
Expand Down Expand Up @@ -259,6 +247,28 @@ def _transfer(self):
self._update_label()
self._transfers += 1

async def _async_sync_data(self):
fileobj = await eval_function_with_deps(self.callback)
self._sync_data(fileobj)

@param.depends('_clicks', watch=True)
def _transfer(self):
if self.file is None and self.callback is None:
if self.embed:
raise ValueError('Must provide a file or a callback '
'if it is to be embedded.')
return

if self.callback is None:
fileobj = self.file
else:
if iscoroutinefunction(self.callback):
state.execute(self._async_sync_data)
return
else:
fileobj = eval_function_with_deps(self.callback)
self._sync_data(fileobj)



class JSONEditor(Widget):
Expand Down

0 comments on commit f73973d

Please sign in to comment.