Skip to content

Commit 0577ef9

Browse files
committed
Server(fix): Preserve owned endpoints after failed cleanup
Check both exit status and stderr before removing an owned server's socket directory. Keep completed failures visible and the endpoint available for retry without changing legacy Server.kill behavior.
1 parent 2bbcbbc commit 0577ef9

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

src/libtmux/server.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,17 @@ def owned(
318318
)
319319
finally:
320320
if socket_path.exists():
321-
Server(
321+
proc = Server(
322322
socket_path=socket_path,
323323
tmux_bin=tmux_bin,
324324
timeout=timeout,
325-
).kill()
325+
).cmd("kill-server")
326+
if (proc.returncode or proc.stderr) and not _is_daemon_not_up_error(
327+
" ".join(proc.stderr)
328+
):
329+
raise exc.LibTmuxException(
330+
proc.stderr or f"Server cleanup exited with {proc.returncode}"
331+
)
326332
shutil.rmtree(directory)
327333

328334
def __enter__(self) -> Self:

tests/test_server.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,52 @@ def run(
518518
shutil.rmtree(socket_path.parent)
519519

520520

521+
def test_owned_server_preserves_socket_after_silent_cleanup_failure(
522+
server: Server,
523+
monkeypatch: pytest.MonkeyPatch,
524+
) -> None:
525+
"""A completed failure without stderr remains visible and retryable."""
526+
run_command = common.run_command
527+
completed = run_command("-V", tmux_bin=server.tmux_bin)
528+
removed: list[pathlib.Path] = []
529+
530+
def run(
531+
*args: object,
532+
tmux_bin: str | None = None,
533+
timeout: float | None = None,
534+
) -> common.CommandResult:
535+
if "kill-server" in args:
536+
return common.CommandResult(
537+
cmd=[str(arg) for arg in args],
538+
stdout=[],
539+
stderr=[],
540+
returncode=7,
541+
process=completed.process,
542+
)
543+
return run_command(*args, tmux_bin=tmux_bin, timeout=timeout)
544+
545+
try:
546+
with monkeypatch.context() as patch:
547+
# Keep the endpoint reachable even if the assertion exposes a regression.
548+
patch.setattr(shutil, "rmtree", removed.append)
549+
with (
550+
pytest.raises(
551+
exc.LibTmuxException, match="Server cleanup exited with 7"
552+
),
553+
Server.owned(tmux_bin=server.tmux_bin) as owned,
554+
):
555+
owned.new_session()
556+
assert owned.socket_path is not None
557+
socket_path = pathlib.Path(owned.socket_path)
558+
patch.setattr(common, "run_command", run)
559+
assert not removed
560+
assert socket_path.exists()
561+
assert owned.is_alive()
562+
finally:
563+
owned.kill()
564+
shutil.rmtree(socket_path.parent)
565+
566+
521567
class StartDirectoryTestFixture(t.NamedTuple):
522568
"""Test fixture for start_directory parameter testing."""
523569

0 commit comments

Comments
 (0)