Skip to content

Commit 857384a

Browse files
committed
Server,Session(fix[version_guard,detach_scope]): raise on unsupported -b; scope detach-client to session
why: Warn-and-skip for -b would silently change semantics to blocking, hanging the caller indefinitely. Session.detach_client() without -s was not actually session-scoped, making it wrong on multi-session servers. what: - confirm_before / command_prompt: replace warnings.warn with LibTmuxException when tmux < 3.3; -b is not optional, dropping it hangs the command queue - Session.detach_client(): restructure arg building so no-target uses -s self.session_id, target_client+all_clients uses -a -t, target_client alone uses -t - Update docstring: no-target now says "detaches all clients in this session" - Update tests: test_detach_client and renamed test now assert 0 clients remain (all session clients detached) rather than before-1
1 parent d7d1707 commit 857384a

File tree

3 files changed

+39
-34
lines changed

3 files changed

+39
-34
lines changed

src/libtmux/server.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -866,19 +866,16 @@ def confirm_before(
866866
... server.cmd('show-options', '-gv', '@cf_test').stdout[0]
867867
'yes'
868868
"""
869-
import warnings
870-
871869
from libtmux.common import has_gte_version
872870

873-
tmux_args: tuple[str, ...] = ()
874-
875-
if has_gte_version("3.3", tmux_bin=self.tmux_bin):
876-
tmux_args += ("-b",)
877-
else:
878-
warnings.warn(
879-
"confirm_before -b requires tmux 3.3+, ignoring",
880-
stacklevel=2,
871+
if not has_gte_version("3.3", tmux_bin=self.tmux_bin):
872+
msg = (
873+
"confirm_before requires tmux 3.3+: -b is always used to avoid "
874+
"blocking the command queue and is not available on this tmux version"
881875
)
876+
raise exc.LibTmuxException(msg)
877+
878+
tmux_args: tuple[str, ...] = ("-b",)
882879

883880
if prompt is not None:
884881
tmux_args += ("-p", prompt)
@@ -953,19 +950,16 @@ def command_prompt(
953950
... server.cmd('show-options', '-gv', '@cp_test').stdout[0]
954951
'hi'
955952
"""
956-
import warnings
957-
958953
from libtmux.common import has_gte_version
959954

960-
tmux_args: tuple[str, ...] = ()
961-
962-
if has_gte_version("3.3", tmux_bin=self.tmux_bin):
963-
tmux_args += ("-b",)
964-
else:
965-
warnings.warn(
966-
"command_prompt -b requires tmux 3.3+, ignoring",
967-
stacklevel=2,
955+
if not has_gte_version("3.3", tmux_bin=self.tmux_bin):
956+
msg = (
957+
"command_prompt requires tmux 3.3+: -b is always used to avoid "
958+
"blocking the command queue and is not available on this tmux version"
968959
)
960+
raise exc.LibTmuxException(msg)
961+
962+
tmux_args: tuple[str, ...] = ("-b",)
969963

970964
if one_key:
971965
tmux_args += ("-1",)

src/libtmux/session.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,12 @@ def detach_client(
269269
Parameters
270270
----------
271271
target_client : str, optional
272-
Target client to detach (``-t`` flag). If omitted, detaches
273-
the most recently active client.
272+
Target client to detach (``-t`` flag). If omitted, all clients
273+
attached to this session are detached (``-s`` session scoping).
274274
all_clients : bool, optional
275-
Detach all clients attached to this session (``-a`` flag). If
276-
combined with ``target_client``, tmux keeps that client attached.
275+
When combined with ``target_client``, detach all clients except
276+
the specified one (``-a -t`` flags). Has no additional effect
277+
when ``target_client`` is omitted.
277278
shell_command : str, optional
278279
Run a shell command after detaching (``-E`` flag).
279280
@@ -284,14 +285,18 @@ def detach_client(
284285
"""
285286
tmux_args: tuple[str, ...] = ()
286287

287-
if all_clients:
288-
tmux_args += ("-a",)
289-
290288
if shell_command is not None:
291289
tmux_args += ("-E", shell_command)
292290

293-
if target_client is not None:
291+
if all_clients and target_client is not None:
292+
# Keep target_client attached; detach all others from session
293+
tmux_args += ("-a", "-t", target_client)
294+
elif target_client is not None:
294295
tmux_args += ("-t", target_client)
296+
else:
297+
# No target specified: scope to this session so behavior is
298+
# deterministic regardless of how many sessions exist on the server
299+
tmux_args += ("-s", str(self.session_id))
295300

296301
proc = self.server.cmd("detach-client", *tmux_args)
297302

tests/test_session.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -586,30 +586,36 @@ def test_detach_client(
586586
session: Session,
587587
server: Server,
588588
) -> None:
589-
"""Test Session.detach_client() detaches the control-mode client."""
589+
"""Test Session.detach_client() detaches all session clients when no target given.
590+
591+
Without target_client, -s session_id scopes the operation to this session.
592+
"""
590593
with control_mode():
591594
before = len(server.list_clients())
592595
assert before > 0
593596
session.detach_client()
594597
after = len(server.list_clients())
595-
assert after == before - 1
598+
assert after == 0
596599

597600

598-
def test_detach_client_only_detaches_one_client(
601+
def test_detach_client_no_target_detaches_all_session_clients(
599602
control_mode: t.Callable[..., t.Any],
600603
session: Session,
601604
server: Server,
602605
) -> None:
603-
"""Test Session.detach_client() without a target detaches one client."""
606+
"""Test Session.detach_client() without a target detaches all session clients.
607+
608+
Without a target_client, the method uses ``-s session_id`` to scope the
609+
operation to this session, detaching all attached clients.
610+
"""
604611
with control_mode(), control_mode():
605612
before = server.cmd("list-clients", "-F", "#{client_name}").stdout
606613
assert len(before) == 2
607614

608615
session.detach_client()
609616

610617
after = server.cmd("list-clients", "-F", "#{client_name}").stdout
611-
assert len(after) == 1
612-
assert set(after) < set(before)
618+
assert len(after) == 0
613619

614620

615621
def test_detach_client_target_client(

0 commit comments

Comments
 (0)