Skip to content

Commit db8a1d3

Browse files
committed
Fluent(feat): Add find_or_create_session
why: give the fluent builder an idempotent session entry, so re-running a build reuses the live session instead of duplicating it -- the tmuxp load -a shape, in the forward-ref builder. what: - PlanBuilder.find_or_create_session(name): record the same create as new_session, made conditional via LazyPlan.ensure with a display-message probe that captures the session's id, first window, and first pane - Cover the recorded shape and a live idempotent double-build
1 parent 3575c87 commit db8a1d3

2 files changed

Lines changed: 59 additions & 0 deletions

File tree

src/libtmux/experimental/fluent.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858
_CURSOR_FMT = "#{cursor_x},#{cursor_y}"
5959
_WAIT_PANE_POLLS = 40
6060
_WAIT_PANE_INTERVAL = 0.05
61+
#: The probe format for a session find-or-create -- the ids
62+
#: ``NewSession(capture_panes=True)`` captures, so a found session binds the
63+
#: same self/window/pane slots a created one would.
64+
_SESSION_PROBE = "#{session_id} #{window_id} #{pane_id}"
6165

6266

6367
def _pane_ready(cursor: str) -> bool:
@@ -158,6 +162,32 @@ def new_session(self, name: str) -> SessionRef:
158162
slot = self.plan.add(NewSession(session_name=name, capture_panes=True))
159163
return SessionRef(self.plan, name, slot)
160164

165+
def find_or_create_session(self, name: str) -> SessionRef:
166+
"""Reach session *name*, creating it only if it does not exist.
167+
168+
At build time this records the same create as :meth:`new_session`, but
169+
makes it conditional (see :meth:`~..ops.plan.LazyPlan.ensure`): at
170+
execution the plan probes for *name* and reuses the live session when it
171+
is already there, so a re-run is idempotent instead of a duplicate.
172+
173+
Examples
174+
--------
175+
>>> from libtmux.experimental.engines.concrete import ConcreteEngine
176+
>>> p = plan()
177+
>>> _ = p.find_or_create_session("dev").window().pane()
178+
>>> [op.kind for op in p.plan.operations]
179+
['new_session']
180+
>>> p.run(ConcreteEngine()).ok
181+
True
182+
"""
183+
create = NewSession(session_name=name, capture_panes=True)
184+
slot = self.plan.add(create)
185+
self.plan.ensure(
186+
slot.slot,
187+
DisplayMessage(target=NameRef(name), message=_SESSION_PROBE),
188+
)
189+
return SessionRef(self.plan, name, slot)
190+
161191
def sleep(self, seconds: float) -> PlanBuilder:
162192
"""Pause *seconds* after the last recorded op (a hard fold boundary).
163193

tests/experimental/test_fluent.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,32 @@ def test_sleep_runs_offline() -> None:
135135
p.sleep(0.0)
136136
pane.split().do(lambda c: c.send_keys("htop"))
137137
assert p.run(ConcreteEngine()).ok
138+
139+
140+
def test_find_or_create_session_records_a_conditional_create() -> None:
141+
"""find_or_create_session records one create, made conditional via ensure."""
142+
p = plan()
143+
pane = p.find_or_create_session("dev").window().pane()
144+
pane.do(lambda c: c.send_keys("vim"))
145+
assert [op.kind for op in p.plan.operations] == ["new_session", "send_keys"]
146+
assert 0 in p.plan._ensures # the create is conditional
147+
148+
149+
def test_find_or_create_session_is_idempotent_live(session: Session) -> None:
150+
"""Building the same session name twice reuses it instead of duplicating."""
151+
from libtmux.experimental.engines.subprocess import SubprocessEngine
152+
153+
engine = SubprocessEngine.for_server(session.server)
154+
155+
def build() -> None:
156+
p = plan()
157+
p.find_or_create_session("fluent-idem").window().pane().do(
158+
lambda c: c.send_keys("echo hi", enter=False),
159+
)
160+
p.run(engine).raise_for_status()
161+
162+
build()
163+
build() # second run must find the existing session, not create a duplicate
164+
165+
named = [s for s in session.server.sessions if s.session_name == "fluent-idem"]
166+
assert len(named) == 1

0 commit comments

Comments
 (0)