Skip to content

Commit 8127286

Browse files
committed
Handling errors
1 parent 40450c0 commit 8127286

File tree

2 files changed

+60
-22
lines changed

2 files changed

+60
-22
lines changed

src/textual/driver.py

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,7 @@ def deliver_binary(
217217
Args:
218218
binary: The binary file to save.
219219
delivery_key: The unique key that was used to deliver the file.
220-
save_path: The location to save the file to. If None,
221-
the default "downloads" directory will be used. When
222-
running via web, only the file name will be used.
220+
save_path: The location to save the file to.
223221
open_method: *web only* Whether to open the file in the browser or
224222
to prompt the user to download it. When running via a standard
225223
(non-web) terminal, this is ignored.
@@ -230,29 +228,52 @@ def deliver_binary(
230228
mime_type: *web only* The MIME type of the file. This will be used to
231229
set the `Content-Type` header in the HTTP response.
232230
"""
231+
232+
def save_file_thread(binary: BinaryIO | TextIO, mode: str) -> None:
233+
try:
234+
with open(save_path, mode) as destination_file:
235+
read = binary.read
236+
write = destination_file.write
237+
chunk_size = 1024 * 64
238+
while True:
239+
data = read(chunk_size)
240+
if not data:
241+
# No data left to read - delivery is complete.
242+
self._delivery_complete(delivery_key, save_path)
243+
break
244+
write(data)
245+
except Exception as error:
246+
# If any exception occurs during the delivery, pass
247+
# it on to the app via a DeliveryFailed event.
248+
self._delivery_failed(delivery_key, exception=error)
249+
finally:
250+
if not binary.closed:
251+
binary.close()
252+
233253
if isinstance(binary, BinaryIO):
234254
mode = "wb"
235255
else:
236256
mode = "w"
237257

238-
def save_file_thread():
239-
with open(save_path, mode) as destination_file:
240-
read = binary.read
241-
write = destination_file.write
242-
chunk_size = 1024 * 64
243-
while True:
244-
data = read(chunk_size)
245-
if not data:
246-
break
247-
write(data)
248-
binary.close()
249-
self._app.call_from_thread(
250-
self._delivery_complete, delivery_key=delivery_key
251-
)
252-
253-
thread = threading.Thread(target=save_file_thread)
258+
thread = threading.Thread(target=save_file_thread, args=(binary, mode))
254259
thread.start()
255260

256-
def _delivery_complete(self, delivery_key: str) -> None:
257-
"""Called when a file has been delivered."""
258-
self._app.post_message(events.DeliveryComplete(delivery_key))
261+
def _delivery_complete(self, delivery_key: str, save_path: Path | None) -> None:
262+
"""Called when a file has been delivered successfully.
263+
264+
Delivers a DeliveryComplete event to the app.
265+
"""
266+
self._app.call_from_thread(
267+
self._app.post_message,
268+
events.DeliveryComplete(key=delivery_key, path=save_path),
269+
)
270+
271+
def _delivery_failed(self, delivery_key: str, exception: BaseException) -> None:
272+
"""Called when a file delivery fails.
273+
274+
Delivers a DeliveryFailed event to the app.
275+
"""
276+
self._app.call_from_thread(
277+
self._app.post_message,
278+
events.DeliveryFailed(key=delivery_key, exception=exception),
279+
)

src/textual/events.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from __future__ import annotations
1515

1616
from dataclasses import dataclass
17+
from pathlib import Path
1718
from typing import TYPE_CHECKING, Type, TypeVar
1819

1920
import rich.repr
@@ -743,3 +744,19 @@ class DeliveryComplete(Event, bubble=False):
743744
744745
This is the same key that was returned by `App.deliver_text`/`App.deliver_binary`.
745746
"""
747+
748+
path: Path | None = None
749+
"""The path where the file was saved, or `None` if the path is not available, for
750+
example if the file was delivered via web browser.
751+
"""
752+
753+
754+
@dataclass
755+
class DeliveryFailed(Event, bubble=False):
756+
"""Sent to App when a file delivery fails."""
757+
758+
key: str
759+
"""The delivery key associated with the delivery."""
760+
761+
exception: BaseException
762+
"""The exception that was raised during the delivery."""

0 commit comments

Comments
 (0)