""" File upload/download widgets
"""
# pylint: disable=no-member
import json
from .base import HTMLBase, T, W
[docs]@W.register
class File(HTMLBase):
"""A file. Might be uploaded from the browser.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
"""
_model_name = T.Unicode("FileModel").tag(sync=True)
_view_name = T.Unicode("FileView").tag(sync=True)
value = T.Bytes(help="The binary representation of the file").tag(
sync=True, **W.trait_types.bytes_serialization
)
name = T.Unicode(help="The file's name").tag(sync=True)
last_modified = T.Int(help="Timestamp of last file modification").tag(sync=True)
size = T.Int(help="The size of the file in bytes").tag(sync=True)
mime_type = T.Unicode(help="The file's MIME type.").tag(sync=True)
[docs] def proxy(self, klass, **kwargs):
"""Generate a Proxy file object, given a File-compatible class"""
traits = ["name", "mime_type", "size", "last_modified", "value"]
for trait in traits:
if trait not in kwargs:
kwargs[trait] = getattr(self, trait)
klass_file = klass(**kwargs)
with klass_file.hold_trait_notifications():
with self.hold_trait_notifications():
for trait in traits:
T.link((self, trait), (klass_file, trait))
return klass_file
[docs]@W.register
class TextFile(File):
"""A Text file"""
_model_name = T.Unicode("TextFileModel").tag(sync=True)
text = T.Unicode().tag(sync=True)
encoding = T.Unicode("utf-8")
def __init__(self, *args, **kwargs):
if "encoding" not in kwargs:
kwargs["encoding"] = "utf-8"
if "text" in kwargs and "value" not in kwargs:
kwargs["value"] = kwargs["text"].decode(kwargs["encoding"])
elif "value" in kwargs and "json" not in kwargs:
kwargs["text"] = kwargs["value"].decode(encoding=kwargs["encoding"])
super().__init__(*args, **kwargs)
with self.hold_trait_notifications():
self.observe(self._on_bytes, "value")
self.observe(self._on_text, "text")
def _on_bytes(self, change):
self.text = change.new.decode(encoding=self.encoding)
def _on_text(self, change):
value = change.new.encode(self.encoding)
size = len(value)
if value is not None and value != self.value:
with self.hold_trait_notifications():
self.value, self.size = value, size
[docs]@W.register
class JSONFile(File):
"""A JSON file"""
_model_name = T.Unicode("JSONFileModel").tag(sync=True)
json = T.Union(
[T.Dict(), T.List(), T.Unicode(), T.Int(), T.Float(), T.Bool()], allow_none=True
).tag(sync=True)
def __init__(self, *args, **kwargs):
if "json" in kwargs and "value" not in kwargs:
try:
kwargs["value"] = json.dumps(
kwargs["json"], indent=2, sort_keys=True
).encode("utf-8")
except json.JSONDecodeError:
pass
elif "value" in kwargs and "json" not in kwargs:
try:
kwargs["json"] = json.loads(kwargs["value"].decode(encoding="utf-8"))
except json.JSONDecodeError:
pass
super().__init__(*args, **kwargs)
with self.hold_trait_notifications():
self.observe(self._on_bytes, "value")
self.observe(self._on_json, "json")
def _on_bytes(self, change):
self.json = json.loads(change.new.decode(encoding="utf-8"))
def _on_json(self, change):
value = json.dumps(change.new, sort_keys=True, indent=2).encode("utf-8")
size = len(value)
if value is not None and value != self.value:
with self.hold_trait_notifications():
self.value, self.size = value, size
[docs]@W.register
class FileBox(HTMLBase, W.Box):
"""A box of files, which can be used to upload and download files"""
_model_name = T.Unicode("FileBoxModel").tag(sync=True)
_view_name = T.Unicode("FileBoxView").tag(sync=True)
children = W.trait_types.TypedTuple(
trait=T.Instance(File), help="List of file widgets"
).tag(sync=True, **W.widget_serialization)
accept = W.trait_types.TypedTuple(
trait=T.Unicode(), help="uploadable extensions and mimetypes (with wildcards)"
).tag(sync=True)
multiple = T.Bool(False, help="if true, accept multiple files").tag(sync=True)