Skip to content

Commit 9833b91

Browse files
committed
feat(rust-backend) add server_kind/control hooks
1 parent b40de3d commit 9833b91

3 files changed

Lines changed: 86 additions & 4 deletions

File tree

.github/workflows/tests.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ jobs:
1212
matrix:
1313
python-version: ['3.14']
1414
tmux-version: ['3.2a', '3.3a', '3.4', '3.5', '3.6', 'master']
15+
backend: ['default']
16+
include:
17+
- python-version: '3.14'
18+
tmux-version: '3.6'
19+
backend: 'rust-control'
1520
steps:
1621
- uses: actions/checkout@v6
1722

@@ -78,6 +83,24 @@ jobs:
7883
export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH
7984
ls $HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin
8085
tmux -V
86+
backend="${{ matrix.backend }}"
87+
if [ "$backend" != "default" ]; then
88+
if ! uv run python - <<'PY'
89+
import importlib.util
90+
import sys
91+
sys.exit(0 if importlib.util.find_spec("vibe_tmux") else 1)
92+
PY
93+
then
94+
echo "vibe_tmux not installed; skipping rust backend tests"
95+
exit 0
96+
fi
97+
export LIBTMUX_BACKEND=rust
98+
export LIBTMUX_RUST_CONNECTION_KIND=protocol
99+
if [ "$backend" = "rust-control" ]; then
100+
export LIBTMUX_RUST_CONTROL_MODE=1
101+
export LIBTMUX_RUST_CONTROL_AUTOSTART=1
102+
fi
103+
fi
81104
uv run py.test --cov=./ --cov-append --cov-report=xml -n auto --verbose
82105
env:
83106
COV_CORE_SOURCE: .

src/libtmux/common.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141

4242
_RUST_BACKEND = os.getenv("LIBTMUX_BACKEND") == "rust"
4343
_RUST_SERVER_CACHE: dict[
44-
tuple[str | None, str | None, int | None, str | None, str | None], t.Any
44+
tuple[str | None, str | None, int | None, str | None, str | None, bool | None],
45+
t.Any,
4546
] = {}
4647
_RUST_SERVER_CONFIG: dict[
47-
tuple[str | None, str | None, int | None, str | None, str | None], set[str]
48+
tuple[str | None, str | None, int | None, str | None, str | None, bool | None],
49+
set[str],
4850
] = {}
4951

5052

@@ -67,6 +69,18 @@ def _resolve_rust_socket_path(socket_path: str | None, socket_name: str | None)
6769
return str(socket_dir / name)
6870

6971

72+
def _env_bool(name: str) -> bool | None:
73+
value = os.getenv(name)
74+
if value is None:
75+
return None
76+
value = value.strip().lower()
77+
if value in {"1", "true", "yes", "on"}:
78+
return True
79+
if value in {"0", "false", "no", "off"}:
80+
return False
81+
return None
82+
83+
7084
def _rust_run_with_config(
7185
socket_path: str | None,
7286
socket_name: str | None,
@@ -167,7 +181,15 @@ def _rust_server(
167181
) -> t.Any:
168182
connection_kind = os.getenv("LIBTMUX_RUST_CONNECTION_KIND")
169183
server_kind = os.getenv("LIBTMUX_RUST_SERVER_KIND")
170-
key = (socket_name, socket_path, colors, connection_kind, server_kind)
184+
control_autostart = _env_bool("LIBTMUX_RUST_CONTROL_AUTOSTART")
185+
key = (
186+
socket_name,
187+
socket_path,
188+
colors,
189+
connection_kind,
190+
server_kind,
191+
control_autostart,
192+
)
171193
server = _RUST_SERVER_CACHE.get(key)
172194
if server is None:
173195
from libtmux import _rust as rust_backend
@@ -177,13 +199,16 @@ def _rust_server(
177199
kwargs["connection_kind"] = connection_kind
178200
if server_kind:
179201
kwargs["server_kind"] = server_kind
202+
if control_autostart is not None:
203+
kwargs["control_autostart"] = control_autostart
180204
with libtmux_trace.span(
181205
"rust_server_init",
182206
layer="python",
183207
socket_name=socket_name,
184208
socket_path=socket_path,
185209
connection_kind=connection_kind,
186210
server_kind=server_kind,
211+
control_autostart=control_autostart,
187212
):
188213
server = rust_backend.Server(
189214
socket_path=socket_path,
@@ -223,6 +248,7 @@ def _rust_cmd_result(
223248

224249
connection_kind = os.getenv("LIBTMUX_RUST_CONNECTION_KIND")
225250
server_kind = os.getenv("LIBTMUX_RUST_SERVER_KIND")
251+
control_autostart = _env_bool("LIBTMUX_RUST_CONTROL_AUTOSTART")
226252
with libtmux_trace.span(
227253
"rust_cmd_result",
228254
layer="python",
@@ -232,13 +258,21 @@ def _rust_cmd_result(
232258
config_file=config_file,
233259
connection_kind=connection_kind,
234260
server_kind=server_kind,
261+
control_autostart=control_autostart,
235262
):
236263
if connection_kind in {"bin", "tmux-bin"} and config_file:
237264
cmd_parts = ["-f", config_file, *cmd_parts]
238265
config_file = None
239266

240267
server = _rust_server(socket_name, socket_path, colors)
241-
key = (socket_name, socket_path, colors, connection_kind, server_kind)
268+
key = (
269+
socket_name,
270+
socket_path,
271+
colors,
272+
connection_kind,
273+
server_kind,
274+
control_autostart,
275+
)
242276
if config_file:
243277
loaded = _RUST_SERVER_CONFIG.setdefault(key, set())
244278
if config_file not in loaded:

src/libtmux/pytest_plugin.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import pytest
1414

1515
from libtmux import exc
16+
from libtmux.common import _rust_server
1617
from libtmux.server import Server
1718
from libtmux.test.constants import TEST_SESSION_PREFIX
1819
from libtmux.test.random import get_test_session_name, namer
@@ -24,6 +25,13 @@
2425
USING_ZSH = "zsh" in os.getenv("SHELL", "")
2526

2627

28+
def _env_truthy(name: str) -> bool:
29+
value = os.getenv(name)
30+
if value is None:
31+
return False
32+
return value.strip().lower() in {"1", "true", "yes", "on"}
33+
34+
2735
@pytest.fixture(scope="session")
2836
def home_path(tmp_path_factory: pytest.TempPathFactory) -> pathlib.Path:
2937
"""Temporary `/home/` path."""
@@ -141,8 +149,25 @@ def server(
141149
>>> result.assert_outcomes(passed=1)
142150
"""
143151
server = Server(socket_name=f"libtmux_test{next(namer)}")
152+
rust_refresh = None
153+
rust_server = None
154+
155+
if os.getenv("LIBTMUX_BACKEND") == "rust" and _env_truthy(
156+
"LIBTMUX_RUST_CONTROL_MODE"
157+
):
158+
socket_path = (
159+
str(server.socket_path)
160+
if isinstance(server.socket_path, pathlib.Path)
161+
else server.socket_path
162+
)
163+
rust_server = _rust_server(server.socket_name, socket_path, server.colors)
164+
rust_refresh = rust_server.subscribe(10)
165+
setattr(server, "_rust_refresh", rust_refresh)
144166

145167
def fin() -> None:
168+
if rust_server is not None:
169+
with contextlib.suppress(Exception):
170+
rust_server.close()
146171
server.kill()
147172

148173
request.addfinalizer(fin)

0 commit comments

Comments
 (0)