Skip to content

Commit e5e6f40

Browse files
committed
refactor: added _FileDrop to prevent repetitions
1 parent fa6cd23 commit e5e6f40

File tree

1 file changed

+56
-69
lines changed

1 file changed

+56
-69
lines changed

solara/components/file_drop.py

Lines changed: 56 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import threading
22
import typing
3-
from typing import Callable, List, Optional, cast
3+
from typing import Callable, List, Optional, Union, cast
44

5-
import ipyvuetify
65
import traitlets
76
from ipyvue import Template
87
from ipyvuetify.extra import FileInput
@@ -30,40 +29,22 @@ class FileDropZone(FileInput):
3029

3130

3231
@solara.component
33-
def FileDrop(
34-
label="Drop file here",
32+
def _FileDrop(
33+
label="Drop file(s) here",
3534
on_total_progress: Optional[Callable[[float], None]] = None,
36-
on_file: Optional[Callable[[FileInfo], None]] = None,
35+
on_file: Optional[Callable[[Union[FileInfo, List[FileInfo]]], None]] = None,
3736
lazy: bool = True,
38-
) -> ipyvuetify.extra.FileInput:
39-
"""Region a user can drop a file into for file uploading.
40-
41-
If lazy=True, no file content will be loaded into memory,
42-
nor will any data be transferred by default.
43-
If lazy=False, file content will be loaded into memory and passed to the `on_file` callback via the `FileInfo.data` attribute.
44-
45-
46-
A file object is of the following argument type:
47-
```python
48-
class FileInfo(typing.TypedDict):
49-
name: str # file name
50-
size: int # file size in bytes
51-
file_obj: typing.BinaryIO
52-
data: Optional[bytes]: bytes # only present if lazy=False
53-
```
54-
55-
56-
## Arguments
57-
* `on_total_progress`: Will be called with the progress in % of the file upload.
58-
* `on_file`: Will be called with a `FileInfo` object, which contains the file `.name`, `.length` and a `.file_obj` object.
59-
* `lazy`: Whether to load the file contents into memory or not. If `False`,
60-
the file contents will be loaded into memory via the `.data` attribute of file object(s).
37+
multiple: bool = False,
38+
):
39+
"""Generic implementation used by FileDrop and FileDropMultiple.
6140
41+
If multiple=True, multiple files can be uploaded.
6242
"""
43+
6344
file_info, set_file_info = solara.use_state(None)
6445
wired_files, set_wired_files = solara.use_state(cast(Optional[typing.List[FileInfo]], None))
6546

66-
file_drop = FileDropZone.element(label=label, on_total_progress=on_total_progress, on_file_info=set_file_info, multiple=False) # type: ignore
47+
file_drop = FileDropZone.element(label=label, on_total_progress=on_total_progress, on_file_info=set_file_info, multiple=multiple) # type: ignore
6748

6849
def wire_files():
6950
if not file_info:
@@ -83,11 +64,15 @@ def handle_file(cancel: threading.Event):
8364
if not wired_files:
8465
return
8566
if on_file:
86-
if not lazy:
87-
wired_files[0]["data"] = wired_files[0]["file_obj"].read()
67+
for i in range(len(wired_files)):
68+
if not lazy:
69+
wired_files[i]["data"] = wired_files[i]["file_obj"].read()
70+
else:
71+
wired_files[i]["data"] = None
72+
if multiple:
73+
on_file(wired_files)
8874
else:
89-
wired_files[0]["data"] = None
90-
on_file(wired_files[0])
75+
on_file(wired_files[0])
9176

9277
result: solara.Result = hooks.use_thread(handle_file, [wired_files])
9378
if result.error:
@@ -96,16 +81,51 @@ def handle_file(cancel: threading.Event):
9681
return file_drop
9782

9883

84+
@solara.component
85+
def FileDrop(
86+
label="Drop file here",
87+
on_total_progress: Optional[Callable[[float], None]] = None,
88+
on_file: Optional[Callable[[FileInfo], None]] = None,
89+
lazy: bool = True,
90+
):
91+
"""Region a user can drop a file into for file uploading.
92+
93+
If lazy=True, no file content will be loaded into memory,
94+
nor will any data be transferred by default.
95+
If lazy=False, file content will be loaded into memory and passed to the `on_file` callback via the `FileInfo.data` attribute.
96+
97+
98+
A file object is of the following argument type:
99+
```python
100+
class FileInfo(typing.TypedDict):
101+
name: str # file name
102+
size: int # file size in bytes
103+
file_obj: typing.BinaryIO
104+
data: Optional[bytes]: bytes # only present if lazy=False
105+
```
106+
107+
108+
## Arguments
109+
* `on_total_progress`: Will be called with the progress in % of the file upload.
110+
* `on_file`: Will be called with a `FileInfo` object, which contains the file `.name`, `.length` and a `.file_obj` object.
111+
* `lazy`: Whether to load the file contents into memory or not. If `False`,
112+
the file contents will be loaded into memory via the `.data` attribute of file object(s).
113+
114+
"""
115+
116+
return _FileDrop(label=label, on_total_progress=on_total_progress, on_file=on_file, lazy=lazy, multiple=False)
117+
118+
99119
@solara.component
100120
def FileDropMultiple(
101121
label="Drop files here",
102122
on_total_progress: Optional[Callable[[float], None]] = None,
103123
on_file: Optional[Callable[[List[FileInfo]], None]] = None,
104124
lazy: bool = True,
105-
) -> ipyvuetify.extra.FileInput:
125+
):
106126
"""Region a user can drop multiple files into for file uploading.
107127
108-
Almost identical to `FileDrop` except that `on_file` is called
128+
Almost identical to `FileDrop` except that multiple files can be dropped and `on_file` is called
109129
with a list of `FileInfo` objects.
110130
111131
## Arguments
@@ -115,38 +135,5 @@ def FileDropMultiple(
115135
* `lazy`: Whether to load the file contents into memory or not.
116136
117137
"""
118-
file_info, set_file_info = solara.use_state(None)
119-
wired_files, set_wired_files = solara.use_state(cast(Optional[typing.List[FileInfo]], None))
120-
121-
file_drop = FileDropZone.element(label=label, on_total_progress=on_total_progress, on_file_info=set_file_info, multiple=True) # type: ignore
122-
123-
def wire_files():
124-
if not file_info:
125-
return
126-
127-
real = cast(FileDropZone, solara.get_widget(file_drop))
128-
129-
# workaround for @observe being cleared
130-
real.version += 1
131-
real.reset_stats()
132-
133-
set_wired_files(cast(typing.List[FileInfo], real.get_files()))
134-
135-
solara.use_side_effect(wire_files, [file_info])
136-
137-
def handle_file(cancel: threading.Event):
138-
if not wired_files:
139-
return
140-
if on_file:
141-
for i in range(len(wired_files)):
142-
if not lazy:
143-
wired_files[i]["data"] = wired_files[i]["file_obj"].read()
144-
else:
145-
wired_files[i]["data"] = None
146-
on_file(wired_files)
147-
148-
result: solara.Result = hooks.use_thread(handle_file, [wired_files])
149-
if result.error:
150-
raise result.error
151138

152-
return file_drop
139+
return _FileDrop(label=label, on_total_progress=on_total_progress, on_file=on_file, lazy=lazy, multiple=True)

0 commit comments

Comments
 (0)