mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-07-25 04:34:07 +02:00
feat: add GUI display availability check for SSH sessions
This commit is contained in:
@@ -7,6 +7,7 @@ import os
|
||||
import select
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
@@ -60,6 +61,40 @@ def _filter_output(output: str) -> str:
|
||||
return output
|
||||
|
||||
|
||||
def check_gui_display_available() -> tuple[bool, str | None]:
|
||||
"""
|
||||
Check whether the current environment can launch the GUI.
|
||||
|
||||
Returns:
|
||||
tuple[bool, str | None]:
|
||||
- ``True, None`` when a graphical display is available.
|
||||
- ``False, <message>`` when GUI startup should be blocked with a helpful message.
|
||||
"""
|
||||
if os.name != "posix" or sys.platform == "darwin":
|
||||
return True, None
|
||||
|
||||
if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"):
|
||||
return True, None
|
||||
|
||||
if (
|
||||
os.environ.get("SSH_CONNECTION")
|
||||
or os.environ.get("SSH_CLIENT")
|
||||
or os.environ.get("SSH_TTY")
|
||||
):
|
||||
return (
|
||||
False,
|
||||
"Cannot start BEC GUI: no graphical display was detected for this SSH session. "
|
||||
"If you want to launch widgets remotely, reconnect with X11 forwarding enabled "
|
||||
"(for example `ssh -X` or `ssh -Y`) or start the GUI from a local graphical session.",
|
||||
)
|
||||
|
||||
return (
|
||||
False,
|
||||
"Cannot start BEC GUI: no graphical display was detected. "
|
||||
"Set `DISPLAY` or `WAYLAND_DISPLAY`, or start the GUI from a graphical session.",
|
||||
)
|
||||
|
||||
|
||||
def _get_output(process, logger, stop_event: threading.Event | None = None) -> None:
|
||||
log_func = {process.stdout: logger.debug, process.stderr: logger.info}
|
||||
stream_buffer = {process.stdout: [], process.stderr: []}
|
||||
@@ -655,6 +690,11 @@ class BECGuiClient(RPCBase):
|
||||
if self._gui_is_alive():
|
||||
self._gui_started_event.set()
|
||||
return
|
||||
gui_available, error_message = check_gui_display_available()
|
||||
if not gui_available:
|
||||
logger.error(error_message)
|
||||
self._startup_timeout = 0
|
||||
return
|
||||
if self._process is None or self._process.poll() is not None:
|
||||
logger.success("GUI starting...")
|
||||
self._startup_timeout = 5
|
||||
|
||||
@@ -12,6 +12,7 @@ from bec_widgets.cli.client_utils import (
|
||||
BECGuiClient,
|
||||
_join_process_output_thread,
|
||||
_start_plot_process,
|
||||
check_gui_display_available,
|
||||
)
|
||||
from bec_widgets.cli.rpc.rpc_base import RPCBase, RPCResponseTimeoutError, rpc_timeout
|
||||
|
||||
@@ -97,6 +98,38 @@ def test_client_utils_passes_client_config_to_server(bec_dispatcher):
|
||||
)
|
||||
|
||||
|
||||
def test_check_gui_display_available_reports_missing_display_for_ssh_session():
|
||||
with mock.patch.dict(
|
||||
"bec_widgets.cli.client_utils.os.environ", {"SSH_CONNECTION": "1"}, clear=True
|
||||
):
|
||||
available, message = check_gui_display_available()
|
||||
|
||||
assert available is False
|
||||
assert message is not None
|
||||
assert "SSH session" in message
|
||||
assert "ssh -X" in message
|
||||
|
||||
|
||||
def test_client_utils_start_server_returns_when_no_display(bec_dispatcher):
|
||||
gui = BECGuiClient()
|
||||
gui._client = bec_dispatcher.client
|
||||
gui._gui_id = "gui_id"
|
||||
gui._gui_is_alive = mock.MagicMock(return_value=False)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"bec_widgets.cli.client_utils.check_gui_display_available",
|
||||
return_value=(False, "No display available"),
|
||||
),
|
||||
mock.patch("bec_widgets.cli.client_utils._start_plot_process") as mock_start_plot,
|
||||
mock.patch("bec_widgets.cli.client_utils.logger") as mock_logger,
|
||||
):
|
||||
gui._start_server(wait=False)
|
||||
|
||||
mock_start_plot.assert_not_called()
|
||||
mock_logger.error.assert_called_once_with("No display available")
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _no_wait_for_server(_client):
|
||||
yield
|
||||
|
||||
Reference in New Issue
Block a user