streamlit-remote 0.7.0__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/PKG-INFO +3 -1
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/README.md +2 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/pyproject.toml +1 -1
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/cli.py +29 -10
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/process.py +9 -1
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/runtime_display.py +179 -12
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/LICENSE +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/__init__.py +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/https.py +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/providers/__init__.py +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/providers/cloudflare.py +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/providers/executables.py +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/providers/ngrok.py +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/py.typed +0 -0
- {streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: streamlit-remote
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Run Streamlit apps with optional remote HTTPS access.
|
|
5
5
|
Author: Yuichiro Tachibana (Tsuchiya)
|
|
6
6
|
Author-email: Yuichiro Tachibana (Tsuchiya) <t.yic.yt@gmail.com>
|
|
@@ -94,6 +94,8 @@ Press `r` to restart only the Streamlit server while keeping the remote tunnel p
|
|
|
94
94
|
|
|
95
95
|
Press `t` to toggle between the live terminal display and plain prefixed log output. When switching to plain output, `st-remote` replays recent logs into the normal terminal buffer so you can copy older logs from terminal or tmux history. You can also press `p` to force plain output and `f` to force the live terminal display. In terminals where single-key input is unavailable, the Enter-based commands still work.
|
|
96
96
|
|
|
97
|
+
If Streamlit or the remote tunnel exits with an error while the live terminal display is active, `st-remote` returns to the normal terminal buffer and replays recent logs before exiting.
|
|
98
|
+
|
|
97
99
|
Use Ctrl+C to stop both Streamlit and the remote tunnel.
|
|
98
100
|
|
|
99
101
|
## Local HTTPS
|
|
@@ -68,6 +68,8 @@ Press `r` to restart only the Streamlit server while keeping the remote tunnel p
|
|
|
68
68
|
|
|
69
69
|
Press `t` to toggle between the live terminal display and plain prefixed log output. When switching to plain output, `st-remote` replays recent logs into the normal terminal buffer so you can copy older logs from terminal or tmux history. You can also press `p` to force plain output and `f` to force the live terminal display. In terminals where single-key input is unavailable, the Enter-based commands still work.
|
|
70
70
|
|
|
71
|
+
If Streamlit or the remote tunnel exits with an error while the live terminal display is active, `st-remote` returns to the normal terminal buffer and replays recent logs before exiting.
|
|
72
|
+
|
|
71
73
|
Use Ctrl+C to stop both Streamlit and the remote tunnel.
|
|
72
74
|
|
|
73
75
|
## Local HTTPS
|
|
@@ -31,7 +31,7 @@ from streamlit_remote.providers.ngrok import (
|
|
|
31
31
|
planned_managed_oauth_policy,
|
|
32
32
|
prepare_managed_oauth_policy,
|
|
33
33
|
)
|
|
34
|
-
from streamlit_remote.runtime_display import make_runtime_display
|
|
34
|
+
from streamlit_remote.runtime_display import STREAMLIT_SOURCE, make_runtime_display
|
|
35
35
|
from streamlit_remote.server import LocalServerConfig, is_port_available, wait_until_listening
|
|
36
36
|
|
|
37
37
|
|
|
@@ -421,7 +421,16 @@ def run(namespace: argparse.Namespace) -> int:
|
|
|
421
421
|
|
|
422
422
|
def start_streamlit_process() -> ManagedProcess:
|
|
423
423
|
display.set_status("Streamlit", "starting")
|
|
424
|
-
|
|
424
|
+
streamlit_env: dict[str, str] = {}
|
|
425
|
+
streamlit_columns = display.streamlit_subprocess_columns()
|
|
426
|
+
if streamlit_columns is not None:
|
|
427
|
+
streamlit_env["COLUMNS"] = str(streamlit_columns)
|
|
428
|
+
return start_logged_process(
|
|
429
|
+
streamlit_command,
|
|
430
|
+
STREAMLIT_SOURCE,
|
|
431
|
+
write_log=display.log,
|
|
432
|
+
env=streamlit_env or None,
|
|
433
|
+
)
|
|
425
434
|
|
|
426
435
|
def restart_streamlit_process(current_handle: ManagedProcess) -> ManagedProcess:
|
|
427
436
|
display.set_status("Streamlit", "restarting")
|
|
@@ -483,11 +492,15 @@ def run(namespace: argparse.Namespace) -> int:
|
|
|
483
492
|
streamlit_handle = restart_streamlit_process(current_handle)
|
|
484
493
|
return streamlit_handle
|
|
485
494
|
|
|
495
|
+
def report_process_error(handle: ManagedProcess, returncode: int) -> None:
|
|
496
|
+
display.report_process_exit(handle.prefix, returncode)
|
|
497
|
+
|
|
486
498
|
return supervise_processes(
|
|
487
499
|
streamlit_handle,
|
|
488
500
|
tunnel_handle,
|
|
489
501
|
restart_requested,
|
|
490
502
|
restart_and_track,
|
|
503
|
+
report_process_error=report_process_error,
|
|
491
504
|
display_toggle_requested=display_toggle_requested,
|
|
492
505
|
toggle_display=display.toggle_display,
|
|
493
506
|
plain_output_requested=plain_output_requested,
|
|
@@ -662,6 +675,7 @@ def supervise_processes(
|
|
|
662
675
|
restart_requested: threading.Event,
|
|
663
676
|
restart_streamlit: Callable[[ManagedProcess], ManagedProcess],
|
|
664
677
|
poll_interval: float = 0.2,
|
|
678
|
+
report_process_error: Callable[[ManagedProcess, int], None] | None = None,
|
|
665
679
|
display_toggle_requested: threading.Event | None = None,
|
|
666
680
|
toggle_display: Callable[[], bool] | None = None,
|
|
667
681
|
plain_output_requested: threading.Event | None = None,
|
|
@@ -695,20 +709,25 @@ def supervise_processes(
|
|
|
695
709
|
continue
|
|
696
710
|
|
|
697
711
|
if tunnel_handle is not None and tunnel_handle.process.poll() is not None:
|
|
698
|
-
|
|
699
|
-
return tunnel_handle.process.returncode
|
|
700
|
-
return 1
|
|
712
|
+
return report_process_returncode(tunnel_handle, report_process_error)
|
|
701
713
|
|
|
702
714
|
if current_streamlit_handle.process.poll() is not None:
|
|
703
|
-
return (
|
|
704
|
-
current_streamlit_handle.process.returncode
|
|
705
|
-
if current_streamlit_handle.process.returncode is not None
|
|
706
|
-
else 1
|
|
707
|
-
)
|
|
715
|
+
return report_process_returncode(current_streamlit_handle, report_process_error)
|
|
708
716
|
|
|
709
717
|
time.sleep(poll_interval)
|
|
710
718
|
|
|
711
719
|
|
|
720
|
+
def report_process_returncode(
|
|
721
|
+
handle: ManagedProcess,
|
|
722
|
+
report_process_error: Callable[[ManagedProcess, int], None] | None,
|
|
723
|
+
) -> int:
|
|
724
|
+
returncode = handle.process.returncode if handle.process.returncode is not None else 1
|
|
725
|
+
if returncode != 0 and report_process_error is not None:
|
|
726
|
+
handle.output_thread.join(timeout=1.0)
|
|
727
|
+
report_process_error(handle, returncode)
|
|
728
|
+
return returncode
|
|
729
|
+
|
|
730
|
+
|
|
712
731
|
def prepare_cli_https_material(namespace: argparse.Namespace) -> HttpsMaterial | None:
|
|
713
732
|
if namespace.dry_run and namespace.https_mode == "self-signed":
|
|
714
733
|
return planned_self_signed_material(namespace.host)
|
|
@@ -3,9 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import threading
|
|
5
5
|
import time
|
|
6
|
-
from collections.abc import Callable, Sequence
|
|
6
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
7
7
|
from contextlib import suppress
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
+
from os import environ
|
|
9
10
|
|
|
10
11
|
LineHandler = Callable[[str], None]
|
|
11
12
|
LinePredicate = Callable[[str], bool]
|
|
@@ -25,7 +26,13 @@ def start_logged_process(
|
|
|
25
26
|
on_line: LineHandler | None = None,
|
|
26
27
|
should_print_line: LinePredicate | None = None,
|
|
27
28
|
write_log: LogWriter | None = None,
|
|
29
|
+
env: Mapping[str, str] | None = None,
|
|
28
30
|
) -> ManagedProcess:
|
|
31
|
+
process_env = None
|
|
32
|
+
if env is not None:
|
|
33
|
+
process_env = environ.copy()
|
|
34
|
+
process_env.update(env)
|
|
35
|
+
|
|
29
36
|
process = subprocess.Popen(
|
|
30
37
|
list(command),
|
|
31
38
|
stdout=subprocess.PIPE,
|
|
@@ -34,6 +41,7 @@ def start_logged_process(
|
|
|
34
41
|
encoding="utf-8",
|
|
35
42
|
errors="replace",
|
|
36
43
|
bufsize=1,
|
|
44
|
+
env=process_env,
|
|
37
45
|
)
|
|
38
46
|
|
|
39
47
|
def pump_output() -> None:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
import sys
|
|
4
5
|
import threading
|
|
5
6
|
from collections import deque
|
|
@@ -12,6 +13,9 @@ from rich.panel import Panel
|
|
|
12
13
|
from rich.table import Table
|
|
13
14
|
from rich.text import Text
|
|
14
15
|
|
|
16
|
+
_ANSI_ESCAPE_RE = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")
|
|
17
|
+
STREAMLIT_SOURCE = "streamlit"
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
class RuntimeDisplay(Protocol):
|
|
17
21
|
def start(self) -> None: ...
|
|
@@ -24,6 +28,8 @@ class RuntimeDisplay(Protocol):
|
|
|
24
28
|
|
|
25
29
|
def toggle_display(self) -> bool: ...
|
|
26
30
|
|
|
31
|
+
def report_process_exit(self, source: str, returncode: int) -> None: ...
|
|
32
|
+
|
|
27
33
|
def set_local_url(self, url: str) -> None: ...
|
|
28
34
|
|
|
29
35
|
def set_remote_url(self, url: str) -> None: ...
|
|
@@ -32,6 +38,8 @@ class RuntimeDisplay(Protocol):
|
|
|
32
38
|
|
|
33
39
|
def set_shortcuts_visible(self, visible: bool) -> None: ...
|
|
34
40
|
|
|
41
|
+
def streamlit_subprocess_columns(self) -> int | None: ...
|
|
42
|
+
|
|
35
43
|
def info(self, message: str) -> None: ...
|
|
36
44
|
|
|
37
45
|
def error(self, message: str) -> None: ...
|
|
@@ -59,6 +67,9 @@ class PlainRuntimeDisplay(RuntimeDisplay):
|
|
|
59
67
|
def toggle_display(self) -> bool:
|
|
60
68
|
return False
|
|
61
69
|
|
|
70
|
+
def report_process_exit(self, source: str, returncode: int) -> None:
|
|
71
|
+
self.error(f"error: {source} exited with code {returncode}.")
|
|
72
|
+
|
|
62
73
|
def set_local_url(self, url: str) -> None:
|
|
63
74
|
self.info("Streamlit local URL:")
|
|
64
75
|
self.info(f" {url}")
|
|
@@ -75,6 +86,9 @@ class PlainRuntimeDisplay(RuntimeDisplay):
|
|
|
75
86
|
self.info("Runtime shortcuts:")
|
|
76
87
|
self.info(" r: restart Streamlit while keeping the tunnel running")
|
|
77
88
|
|
|
89
|
+
def streamlit_subprocess_columns(self) -> int | None:
|
|
90
|
+
return None
|
|
91
|
+
|
|
78
92
|
def info(self, message: str) -> None:
|
|
79
93
|
print(message, file=self._output, flush=True)
|
|
80
94
|
|
|
@@ -92,6 +106,7 @@ class RichRuntimeDisplay(RuntimeDisplay):
|
|
|
92
106
|
_PANEL_HORIZONTAL_OVERHEAD = 4
|
|
93
107
|
_LOG_SOURCE_WIDTH = 11
|
|
94
108
|
_LOG_SOURCE_SEPARATOR_WIDTH = 1
|
|
109
|
+
_RICH_TRACEBACK_CHARS = frozenset("╭╮╰╯│─")
|
|
95
110
|
|
|
96
111
|
def __init__(
|
|
97
112
|
self,
|
|
@@ -137,6 +152,9 @@ class RichRuntimeDisplay(RuntimeDisplay):
|
|
|
137
152
|
def toggle_display(self) -> bool:
|
|
138
153
|
return False
|
|
139
154
|
|
|
155
|
+
def report_process_exit(self, source: str, returncode: int) -> None:
|
|
156
|
+
pass
|
|
157
|
+
|
|
140
158
|
def set_local_url(self, url: str) -> None:
|
|
141
159
|
with self._lock:
|
|
142
160
|
self._local_url = url
|
|
@@ -157,6 +175,11 @@ class RichRuntimeDisplay(RuntimeDisplay):
|
|
|
157
175
|
self._shortcuts_visible = visible
|
|
158
176
|
self._refresh()
|
|
159
177
|
|
|
178
|
+
def streamlit_subprocess_columns(self) -> int | None:
|
|
179
|
+
# Streamlit formats Rich tracebacks before we can render them without a
|
|
180
|
+
# source prefix, so give it the prefix-less log panel width.
|
|
181
|
+
return self._log_panel_content_width()
|
|
182
|
+
|
|
160
183
|
def info(self, message: str) -> None:
|
|
161
184
|
self.log("st-remote", message)
|
|
162
185
|
|
|
@@ -242,9 +265,13 @@ class RichRuntimeDisplay(RuntimeDisplay):
|
|
|
242
265
|
|
|
243
266
|
lines = Text()
|
|
244
267
|
visible_logs = self._visible_logs(panel_height=panel_height)
|
|
268
|
+
raw_traceback_indexes = self._raw_streamlit_traceback_indexes(visible_logs)
|
|
245
269
|
for index, (source, line) in enumerate(visible_logs):
|
|
246
|
-
|
|
247
|
-
|
|
270
|
+
if index in raw_traceback_indexes:
|
|
271
|
+
lines.append(self._format_raw_streamlit_traceback_line(line))
|
|
272
|
+
else:
|
|
273
|
+
lines.append(f"{source:>11} ", style=self._source_style(source))
|
|
274
|
+
lines.append(self._format_log_line(line, width=self._log_line_width()))
|
|
248
275
|
if index < len(visible_logs) - 1:
|
|
249
276
|
lines.append("\n")
|
|
250
277
|
return Panel(lines, title="Logs", height=panel_height)
|
|
@@ -252,24 +279,142 @@ class RichRuntimeDisplay(RuntimeDisplay):
|
|
|
252
279
|
def _visible_logs(self, *, panel_height: int | None = None) -> list[tuple[str, str]]:
|
|
253
280
|
log_panel_height = panel_height or self._layout_heights()[1]
|
|
254
281
|
available_rows = max(1, log_panel_height - self._PANEL_FRAME_ROWS)
|
|
255
|
-
|
|
282
|
+
logs = self._move_interleaved_logs_after_tracebacks(list(self._logs))
|
|
283
|
+
return self._select_visible_logs(logs, available_rows=available_rows)
|
|
256
284
|
|
|
257
|
-
def
|
|
285
|
+
def _select_visible_logs(
|
|
286
|
+
self,
|
|
287
|
+
logs: list[tuple[str, str]],
|
|
288
|
+
*,
|
|
289
|
+
available_rows: int,
|
|
290
|
+
) -> list[tuple[str, str]]:
|
|
291
|
+
if len(logs) <= available_rows:
|
|
292
|
+
return logs
|
|
293
|
+
|
|
294
|
+
traceback_spans = self._streamlit_traceback_spans(logs)
|
|
295
|
+
traceback_span = traceback_spans[-1] if traceback_spans else None
|
|
296
|
+
if traceback_span is None:
|
|
297
|
+
return logs[-available_rows:]
|
|
298
|
+
|
|
299
|
+
start, end = traceback_span
|
|
300
|
+
if any(source == STREAMLIT_SOURCE for source, _ in logs[end:]):
|
|
301
|
+
return logs[-available_rows:]
|
|
302
|
+
|
|
303
|
+
traceback_logs = logs[start:end]
|
|
304
|
+
if len(traceback_logs) >= available_rows:
|
|
305
|
+
return traceback_logs[-available_rows:]
|
|
306
|
+
|
|
307
|
+
following_rows = available_rows - len(traceback_logs)
|
|
308
|
+
following_logs = logs[end:][-following_rows:] if following_rows else []
|
|
309
|
+
selected_logs = [*traceback_logs, *following_logs]
|
|
310
|
+
|
|
311
|
+
preceding_rows = available_rows - len(selected_logs)
|
|
312
|
+
if preceding_rows:
|
|
313
|
+
selected_logs = [*logs[:start][-preceding_rows:], *selected_logs]
|
|
314
|
+
return selected_logs
|
|
315
|
+
|
|
316
|
+
def _move_interleaved_logs_after_tracebacks(
|
|
317
|
+
self,
|
|
318
|
+
logs: list[tuple[str, str]],
|
|
319
|
+
) -> list[tuple[str, str]]:
|
|
320
|
+
arranged_logs: list[tuple[str, str]] = []
|
|
321
|
+
deferred_logs: list[tuple[str, str]] = []
|
|
322
|
+
in_streamlit_traceback = False
|
|
323
|
+
|
|
324
|
+
for source, line in logs:
|
|
325
|
+
if self._is_streamlit_traceback_marker(source, line):
|
|
326
|
+
in_streamlit_traceback = True
|
|
327
|
+
arranged_logs.append((source, line))
|
|
328
|
+
elif in_streamlit_traceback and source != STREAMLIT_SOURCE:
|
|
329
|
+
deferred_logs.append((source, line))
|
|
330
|
+
else:
|
|
331
|
+
arranged_logs.append((source, line))
|
|
332
|
+
|
|
333
|
+
arranged_logs.extend(deferred_logs)
|
|
334
|
+
return arranged_logs
|
|
335
|
+
|
|
336
|
+
def _raw_streamlit_traceback_indexes(self, logs: list[tuple[str, str]]) -> set[int]:
|
|
337
|
+
raw_indexes: set[int] = set()
|
|
338
|
+
for start, end in self._streamlit_traceback_spans(logs):
|
|
339
|
+
raw_indexes.update(range(start, end))
|
|
340
|
+
return raw_indexes
|
|
341
|
+
|
|
342
|
+
def _streamlit_traceback_spans(self, logs: list[tuple[str, str]]) -> list[tuple[int, int]]:
|
|
343
|
+
marker_indexes = [
|
|
344
|
+
index
|
|
345
|
+
for index, (source, line) in enumerate(logs)
|
|
346
|
+
if self._is_streamlit_traceback_marker(source, line)
|
|
347
|
+
]
|
|
348
|
+
spans: list[tuple[int, int]] = []
|
|
349
|
+
for marker_index in marker_indexes:
|
|
350
|
+
start = marker_index
|
|
351
|
+
while start > 0 and logs[start - 1][0] == STREAMLIT_SOURCE:
|
|
352
|
+
start -= 1
|
|
353
|
+
|
|
354
|
+
end = marker_index + 1
|
|
355
|
+
while end < len(logs) and logs[end][0] == STREAMLIT_SOURCE:
|
|
356
|
+
end += 1
|
|
357
|
+
|
|
358
|
+
if spans and start <= spans[-1][1]:
|
|
359
|
+
spans[-1] = (spans[-1][0], max(spans[-1][1], end))
|
|
360
|
+
else:
|
|
361
|
+
spans.append((start, end))
|
|
362
|
+
return spans
|
|
363
|
+
|
|
364
|
+
def _is_streamlit_traceback_marker(self, source: str, line: str) -> bool:
|
|
365
|
+
if source != STREAMLIT_SOURCE:
|
|
366
|
+
return False
|
|
367
|
+
return any(char in _ANSI_ESCAPE_RE.sub("", line) for char in self._RICH_TRACEBACK_CHARS)
|
|
368
|
+
|
|
369
|
+
def _format_raw_streamlit_traceback_line(self, line: str) -> Text:
|
|
370
|
+
text = Text.from_ansi(line)
|
|
371
|
+
plain = text.plain
|
|
372
|
+
start = 0
|
|
373
|
+
end = len(plain)
|
|
374
|
+
|
|
375
|
+
leading_border_match = re.match(r"^\s*│ ?", plain)
|
|
376
|
+
if leading_border_match is not None:
|
|
377
|
+
start = leading_border_match.end()
|
|
378
|
+
|
|
379
|
+
trailing_border_match = re.search(r"\s+│\s*$", plain)
|
|
380
|
+
if trailing_border_match is not None and trailing_border_match.start() >= start:
|
|
381
|
+
end = trailing_border_match.start()
|
|
382
|
+
|
|
383
|
+
end = min(end, len(plain[:end].rstrip()))
|
|
384
|
+
formatted_text = text[start:end]
|
|
385
|
+
return self._format_raw_streamlit_traceback_text(formatted_text)
|
|
386
|
+
|
|
387
|
+
def _format_raw_streamlit_traceback_text(self, text: Text) -> Text:
|
|
388
|
+
width = self._log_panel_content_width()
|
|
389
|
+
formatted_text = text.copy()
|
|
390
|
+
if len(formatted_text.plain) > width:
|
|
391
|
+
if width <= 3:
|
|
392
|
+
formatted_text = Text("." * width)
|
|
393
|
+
else:
|
|
394
|
+
formatted_text = formatted_text[: width - 3]
|
|
395
|
+
formatted_text.append("...")
|
|
396
|
+
formatted_text.pad_right(width - len(formatted_text.plain))
|
|
397
|
+
return formatted_text
|
|
398
|
+
|
|
399
|
+
def _format_log_line(self, line: str, *, width: int) -> str:
|
|
258
400
|
line = line.replace("\n", r"\n").replace("\r", r"\r")
|
|
259
|
-
|
|
260
|
-
if len(line) <= line_width:
|
|
401
|
+
if len(line) <= width:
|
|
261
402
|
return line
|
|
262
|
-
if
|
|
263
|
-
return "." *
|
|
264
|
-
return f"{line[:
|
|
403
|
+
if width <= 3:
|
|
404
|
+
return "." * width
|
|
405
|
+
return f"{line[: width - 3]}..."
|
|
265
406
|
|
|
266
407
|
def _log_line_width(self) -> int:
|
|
267
|
-
content_width = self._console.size.width - self._PANEL_HORIZONTAL_OVERHEAD
|
|
268
408
|
return max(
|
|
269
409
|
1,
|
|
270
|
-
|
|
410
|
+
self._log_panel_content_width()
|
|
411
|
+
- self._LOG_SOURCE_WIDTH
|
|
412
|
+
- self._LOG_SOURCE_SEPARATOR_WIDTH,
|
|
271
413
|
)
|
|
272
414
|
|
|
415
|
+
def _log_panel_content_width(self) -> int:
|
|
416
|
+
return max(1, self._console.size.width - self._PANEL_HORIZONTAL_OVERHEAD)
|
|
417
|
+
|
|
273
418
|
def _render_shortcuts_panel(self, *, height: int | None = None) -> Panel:
|
|
274
419
|
if not self._shortcuts_visible:
|
|
275
420
|
return Panel(
|
|
@@ -288,7 +433,7 @@ class RichRuntimeDisplay(RuntimeDisplay):
|
|
|
288
433
|
return Panel(shortcuts, title="Shortcuts", border_style="green", height=height)
|
|
289
434
|
|
|
290
435
|
def _source_style(self, source: str) -> str:
|
|
291
|
-
if source ==
|
|
436
|
+
if source == STREAMLIT_SOURCE:
|
|
292
437
|
return "blue"
|
|
293
438
|
if source in {"cloudflared", "ngrok"}:
|
|
294
439
|
return "magenta"
|
|
@@ -381,6 +526,24 @@ class SwitchableRuntimeDisplay(RuntimeDisplay):
|
|
|
381
526
|
return self.switch_to_plain()
|
|
382
527
|
return self.switch_to_rich()
|
|
383
528
|
|
|
529
|
+
def report_process_exit(self, source: str, returncode: int) -> None:
|
|
530
|
+
with self._lock:
|
|
531
|
+
if self._active_display is self._plain_display:
|
|
532
|
+
self._plain_display.report_process_exit(source, returncode)
|
|
533
|
+
return
|
|
534
|
+
|
|
535
|
+
was_started = self._started
|
|
536
|
+
if was_started:
|
|
537
|
+
self._rich_display.stop()
|
|
538
|
+
self._active_display = self._plain_display
|
|
539
|
+
if was_started:
|
|
540
|
+
self._plain_display.start()
|
|
541
|
+
|
|
542
|
+
self._plain_display.report_process_exit(source, returncode)
|
|
543
|
+
self._plain_display.info("Recent logs:")
|
|
544
|
+
for log_source, line in self._logs:
|
|
545
|
+
self._plain_display.log(log_source, line)
|
|
546
|
+
|
|
384
547
|
def set_local_url(self, url: str) -> None:
|
|
385
548
|
with self._lock:
|
|
386
549
|
self._local_url = url
|
|
@@ -401,6 +564,10 @@ class SwitchableRuntimeDisplay(RuntimeDisplay):
|
|
|
401
564
|
self._shortcuts_visible = visible
|
|
402
565
|
self._active_display.set_shortcuts_visible(visible)
|
|
403
566
|
|
|
567
|
+
def streamlit_subprocess_columns(self) -> int | None:
|
|
568
|
+
with self._lock:
|
|
569
|
+
return self._active_display.streamlit_subprocess_columns()
|
|
570
|
+
|
|
404
571
|
def info(self, message: str) -> None:
|
|
405
572
|
with self._lock:
|
|
406
573
|
self._logs.append(("st-remote", message))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/providers/__init__.py
RENAMED
|
File without changes
|
{streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/providers/cloudflare.py
RENAMED
|
File without changes
|
{streamlit_remote-0.7.0 → streamlit_remote-0.7.2}/src/streamlit_remote/providers/executables.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|