Skip to content

Commit 6a03ad9

Browse files
authored
feature(window,session): Allow passing environment variables (#453)
For Window.split_window and Session.new_window in tmux 3.0+
2 parents c425c37 + fca7c0d commit 6a03ad9

File tree

5 files changed

+143
-2
lines changed

5 files changed

+143
-2
lines changed

CHANGES

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ $ pip install --user --upgrade --pre libtmux
1414

1515
<!-- Maintainers and contributors: Insert change notes for the next release above -->
1616

17+
### Features
18+
19+
- `Window.split_window()` and `Session.new_window()` now support an optional
20+
dictionary of environmental variables, via (#453), credit @zappolowski.
21+
1722
## libtmux 0.15.10 (2022-11-05)
1823

1924
_There will be more improvements over the coming weeks and months to shore up

src/libtmux/session.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
TmuxRelationalObject,
2020
WindowDict,
2121
handle_option_error,
22+
has_gte_version,
2223
has_version,
2324
session_check_name,
2425
)
@@ -202,6 +203,7 @@ def new_window(
202203
attach: bool = True,
203204
window_index: str = "",
204205
window_shell: t.Optional[str] = None,
206+
environment: t.Optional[t.Dict[str, str]] = None,
205207
) -> Window:
206208
"""
207209
Return :class:`Window` from ``$ tmux new-window``.
@@ -227,6 +229,9 @@ def new_window(
227229
When this command exits the window will close. This feature is
228230
useful for long-running processes where the closing of the
229231
window upon completion is desired.
232+
environment: dict, optional
233+
Environmental variables for new window. tmux 3.0+ only. Passthrough to
234+
``-e``.
230235
231236
Returns
232237
-------
@@ -259,6 +264,15 @@ def new_window(
259264
% (self.id, window_index),
260265
)
261266

267+
if environment:
268+
if has_gte_version("3.0"):
269+
for k, v in environment.items():
270+
window_args += (f"-e{k}={v}",)
271+
else:
272+
logger.warning(
273+
"Cannot set up environment as tmux 3.0 or newer is required."
274+
)
275+
262276
if window_shell:
263277
window_args += (window_shell,)
264278

src/libtmux/window.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import shlex
1010
import typing as t
1111

12-
from libtmux.common import tmux_cmd
12+
from libtmux.common import has_gte_version, tmux_cmd
1313
from libtmux.pane import Pane
1414

1515
from . import exc, formats
@@ -442,6 +442,7 @@ def split_window(
442442
vertical: bool = True,
443443
shell: t.Optional[str] = None,
444444
percent: t.Optional[int] = None,
445+
environment: t.Optional[t.Dict[str, str]] = None,
445446
) -> Pane:
446447
"""
447448
Split window and return the created :class:`Pane`.
@@ -468,6 +469,8 @@ def split_window(
468469
window upon completion is desired.
469470
percent: int, optional
470471
percentage to occupy with respect to current window
472+
environment: dict, optional
473+
Environmental variables for new pane. tmux 3.0+ only. Passthrough to ``-e``.
471474
472475
Returns
473476
-------
@@ -520,6 +523,15 @@ def split_window(
520523
if not attach:
521524
tmux_args += ("-d",)
522525

526+
if environment:
527+
if has_gte_version("3.0"):
528+
for k, v in environment.items():
529+
tmux_args += (f"-e{k}={v}",)
530+
else:
531+
logger.warning(
532+
"Cannot set up environment as tmux 3.0 or newer is required."
533+
)
534+
523535
if shell:
524536
tmux_args += (shell,)
525537

tests/test_session.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"""Test for libtmux Session object."""
22
import logging
3+
import shutil
34
import typing as t
45

56
import pytest
67

78
from libtmux import exc
8-
from libtmux.common import has_gte_version
9+
from libtmux.common import has_gte_version, has_lt_version
910
from libtmux.pane import Pane
1011
from libtmux.server import Server
1112
from libtmux.session import Session
@@ -257,3 +258,57 @@ def test_cmd_inserts_sesion_id(session: Session) -> None:
257258
assert "-t" in cmd.cmd
258259
assert current_session_id in cmd.cmd
259260
assert cmd.cmd[-1] == last_arg
261+
262+
263+
@pytest.mark.skipif(
264+
has_lt_version("3.0"),
265+
reason="needs -e flag for new-window which was introduced in 3.0",
266+
)
267+
@pytest.mark.parametrize(
268+
"environment",
269+
[
270+
{"ENV_VAR": "window"},
271+
{"ENV_VAR_1": "window_1", "ENV_VAR_2": "window_2"},
272+
],
273+
)
274+
def test_new_window_with_environment(
275+
session: Session,
276+
environment: t.Dict[str, str],
277+
) -> None:
278+
env = shutil.which("env")
279+
assert env is not None, "Cannot find usable `env` in PATH."
280+
281+
window = session.new_window(
282+
attach=True,
283+
window_name="window_with_environment",
284+
window_shell=f"{env} PS1='$ ' sh",
285+
environment=environment,
286+
)
287+
pane = window.attached_pane
288+
assert pane is not None
289+
for k, v in environment.items():
290+
pane.send_keys(f"echo ${k}")
291+
assert pane.capture_pane()[-2] == v
292+
293+
294+
@pytest.mark.skipif(
295+
has_gte_version("3.0"),
296+
reason="3.0 has the -e flag on new-window",
297+
)
298+
def test_new_window_with_environment_logs_warning_for_old_tmux(
299+
session: Session,
300+
caplog: pytest.LogCaptureFixture,
301+
) -> None:
302+
env = shutil.which("env")
303+
assert env is not None, "Cannot find usable `env` in PATH."
304+
305+
session.new_window(
306+
attach=True,
307+
window_name="window_with_environment",
308+
window_shell=f"{env} PS1='$ ' sh",
309+
environment={"ENV_VAR": "window"},
310+
)
311+
312+
assert any(
313+
"Cannot set up environment" in record.msg for record in caplog.records
314+
), "Warning missing"

tests/test_window.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Test for libtmux Window object."""
22
import logging
3+
import shutil
4+
import time
35
import typing as t
46

57
import pytest
@@ -310,3 +312,56 @@ def test_empty_window_name(session: Session) -> None:
310312
"#{==:#{session_name}," + session.name + "}",
311313
)
312314
assert "''" in cmd.stdout
315+
316+
317+
@pytest.mark.skipif(
318+
has_lt_version("3.0"),
319+
reason="needs -e flag for split-window which was introduced in 3.0",
320+
)
321+
@pytest.mark.parametrize(
322+
"environment",
323+
[
324+
{"ENV_VAR": "pane"},
325+
{"ENV_VAR_1": "pane_1", "ENV_VAR_2": "pane_2"},
326+
],
327+
)
328+
def test_split_window_with_environment(
329+
session: Session,
330+
environment: t.Dict[str, str],
331+
) -> None:
332+
env = shutil.which("env")
333+
assert env is not None, "Cannot find usable `env` in Path."
334+
335+
window = session.new_window(window_name="split_window_with_environment")
336+
pane = window.split_window(
337+
shell=f"{env} PS1='$ ' sh",
338+
environment=environment,
339+
)
340+
assert pane is not None
341+
# wait a bit for the prompt to be ready as the test gets flaky otherwise
342+
time.sleep(0.05)
343+
for k, v in environment.items():
344+
pane.send_keys(f"echo ${k}")
345+
assert pane.capture_pane()[-2] == v
346+
347+
348+
@pytest.mark.skipif(
349+
has_gte_version("3.0"),
350+
reason="3.0 has the -e flag on split-window",
351+
)
352+
def test_split_window_with_environment_logs_warning_for_old_tmux(
353+
session: Session,
354+
caplog: pytest.LogCaptureFixture,
355+
) -> None:
356+
env = shutil.which("env")
357+
assert env is not None, "Cannot find usable `env` in Path."
358+
359+
window = session.new_window(window_name="split_window_with_environment")
360+
window.split_window(
361+
shell=f"{env} PS1='$ ' sh",
362+
environment={"ENV_VAR": "pane"},
363+
)
364+
365+
assert any(
366+
"Cannot set up environment" in record.msg for record in caplog.records
367+
), "Warning missing"

0 commit comments

Comments
 (0)