Skip to content

Commit c046d1b

Browse files
committed
feat(Server): add connect() for simplified session management
Adds Server.connect(session_name) that returns an existing session or creates a new detached session if it doesn't exist. This provides a cleaner API for common session reuse patterns, working transparently with both subprocess and control-mode engines.
1 parent 43f8021 commit c046d1b

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

src/libtmux/server.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,78 @@ def attach_session(self, target_session: str | None = None) -> None:
436436
if proc.stderr:
437437
raise exc.LibTmuxException(proc.stderr)
438438

439+
def connect(self, session_name: str) -> Session:
440+
"""Connect to a session, creating if it doesn't exist.
441+
442+
Returns an existing session if found, otherwise creates a new detached session.
443+
444+
Parameters
445+
----------
446+
session_name : str
447+
Name of the session to connect to.
448+
449+
Returns
450+
-------
451+
:class:`Session`
452+
The connected or newly created session.
453+
454+
Raises
455+
------
456+
:exc:`exc.BadSessionName`
457+
If the session name is invalid (contains '.' or ':').
458+
:exc:`exc.LibTmuxException`
459+
If tmux returns an error.
460+
461+
Examples
462+
--------
463+
>>> session = server.connect('my_session')
464+
>>> session.name
465+
'my_session'
466+
467+
Calling again returns the same session:
468+
469+
>>> session2 = server.connect('my_session')
470+
>>> session2.session_id == session.session_id
471+
True
472+
"""
473+
session_check_name(session_name)
474+
475+
# Check if session already exists
476+
if self.has_session(session_name):
477+
session = self.sessions.get(session_name=session_name)
478+
if session is None:
479+
msg = "Session lookup failed after has_session passed"
480+
raise exc.LibTmuxException(msg)
481+
return session
482+
483+
# Session doesn't exist, create it
484+
# Save and clear TMUX env var (same as new_session)
485+
env = os.environ.get("TMUX")
486+
if env:
487+
del os.environ["TMUX"]
488+
489+
proc = self.cmd(
490+
"new-session",
491+
"-d",
492+
f"-s{session_name}",
493+
"-P",
494+
"-F#{session_id}",
495+
)
496+
497+
if proc.stderr:
498+
raise exc.LibTmuxException(proc.stderr)
499+
500+
session_id = proc.stdout[0]
501+
502+
# Restore TMUX env var
503+
if env:
504+
os.environ["TMUX"] = env
505+
506+
return Session.from_session_id(
507+
server=self,
508+
session_id=session_id,
509+
)
510+
439511
def new_session(
440512
self,
441513
session_name: str | None = None,

tests/test_server.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import pytest
1313

14+
from libtmux import exc
1415
from libtmux.common import has_gte_version, has_version
1516
from libtmux.server import Server
1617

@@ -164,6 +165,35 @@ def test_new_session_shell_env(server: Server) -> None:
164165
assert pane_start_command == cmd
165166

166167

168+
def test_connect_creates_new_session(server: Server) -> None:
169+
"""Server.connect creates a new session when it doesn't exist."""
170+
session = server.connect("test_connect_new")
171+
assert session.name == "test_connect_new"
172+
assert session.session_id is not None
173+
174+
175+
def test_connect_reuses_existing_session(server: Server, session: Session) -> None:
176+
"""Server.connect reuses an existing session instead of creating a new one."""
177+
# First call creates
178+
session1 = server.connect("test_connect_reuse")
179+
assert session1.name == "test_connect_reuse"
180+
session_id_1 = session1.session_id
181+
182+
# Second call should return the same session
183+
session2 = server.connect("test_connect_reuse")
184+
assert session2.session_id == session_id_1
185+
assert session2.name == "test_connect_reuse"
186+
187+
188+
def test_connect_invalid_name(server: Server) -> None:
189+
"""Server.connect raises BadSessionName for invalid session names."""
190+
with pytest.raises(exc.BadSessionName):
191+
server.connect("invalid.name")
192+
193+
with pytest.raises(exc.BadSessionName):
194+
server.connect("invalid:name")
195+
196+
167197
@pytest.mark.skipif(has_version("3.2"), reason="Wrong width returned with 3.2")
168198
def test_new_session_width_height(server: Server) -> None:
169199
"""Verify ``Server.new_session`` creates valid session running w/ dimensions."""

0 commit comments

Comments
 (0)