Skip to content

Commit 7c2437f

Browse files
authored
feat: Support sleep_before, sleep_after (#750)
2 parents 5bfb779 + 89bfa9e commit 7c2437f

11 files changed

+313
-3
lines changed

CHANGES

+21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,27 @@
2222
enter: false
2323
```
2424
25+
- #750: Pause execution via `sleep_before: [int]` and `sleep_after: [int]`
26+
27+
```yaml
28+
session_name: Pause / skip command execution (command-level)
29+
windows:
30+
- panes:
31+
- shell_command:
32+
# Executes immediately
33+
- echo "___$((11 + 1))___"
34+
# Delays before sending 2 seconds
35+
- cmd: echo "___$((1 + 3))___"
36+
sleep_before: 2
37+
# Executes immediately
38+
- cmd: echo "___$((1 + 3))___"
39+
# Pauses 2 seconds after
40+
- cmd: echo "Stuff rendering here!"
41+
sleep_after: 2
42+
# Executes after earlier commands (after 2 sec)
43+
- cmd: echo "2 seconds later"
44+
```
45+
2546
- #701: `tmuxp freeze` now accepts `--quiet` and `--yes` along with the
2647
`--config-format` and filename (`--save-to`). This means you can do it all in
2748
one command:

docs/examples.md

+60
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ This will add the `shell_command` to the bash history in the pane.
332332

333333
```{versionadded} 1.10.0b1
334334
`enter: false` option. Pane-level support.
335+
```
336+
335337
```{versionadded} 1.10.0b3
336338
Support command-level skipping.
337339
```
@@ -375,6 +377,64 @@ Omit sending {kbd}`enter` to key commands. Equivalent to
375377
376378
````
377379

380+
## Pausing commands
381+
382+
```{versionadded} 1.10.0b4
383+
`sleep_before` and `sleep_after` options added. Pane and command-level support.
384+
```
385+
386+
```{warning}
387+
This will delay loading as it runs synchronously for each pane. In future version asynchronous support, (i.e. [`asyncio`](asyncio)) will speed up this up.
388+
```
389+
390+
Omit sending {kbd}`enter` to key commands. Equivalent to having
391+
a [`time.sleep`](time.sleep) before and after [`send_keys`](libtmux.Pane.send_keys).
392+
393+
This is especially useful for expensive commands where the terminal needs some breathing room (virtualenv, poetry, pipenv, sourcing a configuration, launching a tui app, etc).
394+
395+
````{tab} Virtualenv
396+
397+
```{literalinclude} ../examples/sleep-virtualenv.yaml
398+
:language: yaml
399+
400+
```
401+
````
402+
403+
````{tab} YAML
404+
405+
```{literalinclude} ../examples/sleep.yaml
406+
:language: yaml
407+
408+
```
409+
410+
````
411+
412+
````{tab} JSON
413+
414+
```{literalinclude} ../examples/sleep.json
415+
:language: json
416+
417+
```
418+
419+
````
420+
421+
````{tab} YAML (pane-level)
422+
423+
```{literalinclude} ../examples/sleep-pane-level.yaml
424+
:language: yaml
425+
426+
```
427+
428+
````
429+
430+
````{tab} JSON (pane-level)
431+
432+
```{literalinclude} ../examples/sleep-pane-level.json
433+
:language: json
434+
435+
```
436+
437+
````
378438

379439
## Window Index
380440

examples/skip-send-pane-level.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"session_name": "Skip all pane commands",
2+
"session_name": "Skip command execution (pane-level)",
33
"windows": [
44
{
55
"panes": [
@@ -17,4 +17,4 @@
1717
]
1818
}
1919
]
20-
}
20+
}

examples/skip-send-pane-level.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
session_name: Skip all pane commands
1+
session_name: Skip command execution (pane-level)
22
windows:
33
- panes:
44
- shell_command: echo "___$((1 + 3))___"

examples/sleep-pane-level.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"session_name": "Pause / skip command execution (pane-level)",
3+
"windows": [
4+
{
5+
"panes": [
6+
{
7+
"sleep_before": 2,
8+
"shell_command": [
9+
"echo \"___$((11 + 1))___\"",
10+
{
11+
"cmd": "echo \"___$((1 + 3))___\""
12+
},
13+
{
14+
"cmd": "echo \"___$((1 + 3))___\""
15+
},
16+
{
17+
"cmd": "echo \"Stuff rendering here!\""
18+
},
19+
{
20+
"cmd": "echo \"2 seconds later\""
21+
}
22+
]
23+
}
24+
]
25+
}
26+
]
27+
}

examples/sleep-pane-level.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
session_name: Pause / skip command execution (pane-level)
2+
windows:
3+
- panes:
4+
-
5+
# Wait 2 seconds before sending all commands in this pane
6+
sleep_before: 2
7+
shell_command:
8+
- echo "___$((11 + 1))___"
9+
- cmd: echo "___$((1 + 3))___"
10+
- cmd: echo "___$((1 + 3))___"
11+
- cmd: echo "Stuff rendering here!"
12+
- cmd: echo "2 seconds later"
13+

examples/sleep-virtualenv.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
session_name: virtualenv
2+
shell_command_before:
3+
# - cmd: source $(poetry env info --path)/bin/activate
4+
# - cmd: source `pipenv --venv`/bin/activate
5+
- cmd: source .venv/bin/activate
6+
sleep_before: 1
7+
sleep_after: 1
8+
windows:
9+
- panes:
10+
- shell_command:
11+
- ./manage.py runserver

examples/sleep.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"session_name": "Pause / skip command execution (command-level)",
3+
"windows": [
4+
{
5+
"panes": [
6+
{
7+
"shell_command": [
8+
"echo \"___$((11 + 1))___\"",
9+
{
10+
"cmd": "echo \"___$((1 + 3))___\"",
11+
"sleep_before": 2
12+
},
13+
{
14+
"cmd": "echo \"___$((1 + 3))___\""
15+
},
16+
{
17+
"cmd": "echo \"Stuff rendering here!\"",
18+
"sleep_after": 2
19+
},
20+
{
21+
"cmd": "echo \"2 seconds later\""
22+
}
23+
]
24+
}
25+
]
26+
}
27+
]
28+
}

examples/sleep.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
session_name: Pause / skip command execution (command-level)
2+
windows:
3+
- panes:
4+
- shell_command:
5+
# Executes immediately
6+
- echo "___$((11 + 1))___"
7+
# Delays before sending 2 seconds
8+
- cmd: echo "___$((1 + 3))___"
9+
sleep_before: 2
10+
# Executes immediately
11+
- cmd: echo "___$((1 + 3))___"
12+
# Pauses 2 seconds after
13+
- cmd: echo "Stuff rendering here!"
14+
sleep_after: 2
15+
# Executes after earlier commands (after 2 sec)
16+
- cmd: echo "2 seconds later"

tests/test_workspacebuilder.py

+123
Original file line numberDiff line numberDiff line change
@@ -1021,3 +1021,126 @@ def test_load_workspace_enter(
10211021
assert output in captured_pane
10221022
else:
10231023
assert output not in captured_pane
1024+
1025+
1026+
@pytest.mark.parametrize(
1027+
"yaml,sleep,output",
1028+
[
1029+
[
1030+
textwrap.dedent(
1031+
"""
1032+
session_name: Should not execute
1033+
windows:
1034+
- panes:
1035+
- shell_command:
1036+
- cmd: echo "___$((1 + 5))___"
1037+
sleep_before: 2
1038+
- cmd: echo "___$((1 + 3))___"
1039+
sleep_before: 1
1040+
"""
1041+
),
1042+
1.5,
1043+
"___4___",
1044+
],
1045+
[
1046+
textwrap.dedent(
1047+
"""
1048+
session_name: Should not execute
1049+
windows:
1050+
- panes:
1051+
- shell_command:
1052+
- cmd: echo "___$((1 + 5))___"
1053+
sleep_before: 2
1054+
- cmd: echo "___$((1 + 3))___"
1055+
sleep_before: 1
1056+
"""
1057+
),
1058+
3,
1059+
"___4___",
1060+
],
1061+
[
1062+
textwrap.dedent(
1063+
"""
1064+
session_name: Should not execute
1065+
windows:
1066+
- panes:
1067+
- shell_command:
1068+
- cmd: echo "___$((1 + 3))___"
1069+
sleep_before: 2
1070+
"""
1071+
),
1072+
2,
1073+
"___4___",
1074+
],
1075+
[
1076+
textwrap.dedent(
1077+
"""
1078+
session_name: Should not execute
1079+
windows:
1080+
- panes:
1081+
- shell_command:
1082+
- cmd: echo "___$((1 + 3))___"
1083+
sleep_before: 2
1084+
"""
1085+
),
1086+
2,
1087+
"___4___",
1088+
],
1089+
[
1090+
textwrap.dedent(
1091+
"""
1092+
session_name: Should not execute
1093+
shell_command_before:
1094+
- cmd: echo "sleeping before"
1095+
sleep_before: 2
1096+
windows:
1097+
- panes:
1098+
- echo "___$((1 + 3))___"
1099+
"""
1100+
),
1101+
2,
1102+
"___4___",
1103+
],
1104+
],
1105+
ids=[
1106+
"command_level_sleep_3_shortform",
1107+
"command_level_pane_sleep_3_longform",
1108+
"pane_sleep_2_shortform",
1109+
"pane_sleep_2_longform",
1110+
"shell_before_before_command_level",
1111+
],
1112+
)
1113+
@pytest.mark.flaky(reruns=3)
1114+
def test_load_workspace_sleep(
1115+
tmp_path: pathlib.Path,
1116+
server: libtmux.Server,
1117+
monkeypatch: pytest.MonkeyPatch,
1118+
yaml,
1119+
sleep: int,
1120+
output,
1121+
):
1122+
yaml_config = tmp_path / "simple.yaml"
1123+
yaml_config.write_text(
1124+
yaml,
1125+
encoding="utf-8",
1126+
)
1127+
sconfig = kaptan.Kaptan(handler="yaml")
1128+
sconfig = sconfig.import_config(str(yaml_config)).get()
1129+
sconfig = config.expand(sconfig)
1130+
sconfig = config.trickle(sconfig)
1131+
builder = WorkspaceBuilder(sconf=sconfig, server=server)
1132+
builder.build()
1133+
1134+
t = time.process_time()
1135+
1136+
time.sleep(1)
1137+
session = builder.session
1138+
pane = session.attached_pane
1139+
1140+
while (time.process_time() - t) * 1000 < sleep:
1141+
captured_pane = "\n".join(pane.capture_pane())
1142+
1143+
assert output not in captured_pane
1144+
time.sleep(0.1)
1145+
captured_pane = "\n".join(pane.capture_pane())
1146+
assert output in captured_pane

tmuxp/workspacebuilder.py

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
"""
77
import logging
8+
import time
89

910
from libtmux.exc import TmuxSessionExists
1011
from libtmux.pane import Pane
@@ -363,11 +364,21 @@ def get_pane_shell():
363364
suppress = True
364365

365366
enter = pconf.get("enter", True)
367+
sleep_before = pconf.get("sleep_before", None)
368+
sleep_after = pconf.get("sleep_after", None)
366369
for cmd in pconf["shell_command"]:
367370
enter = cmd.get("enter", enter)
371+
sleep_before = cmd.get("sleep_before", sleep_before)
372+
sleep_after = cmd.get("sleep_after", sleep_after)
373+
374+
if sleep_before is not None:
375+
time.sleep(sleep_before)
368376

369377
p.send_keys(cmd["cmd"], suppress_history=suppress, enter=enter)
370378

379+
if sleep_after is not None:
380+
time.sleep(sleep_after)
381+
371382
if "focus" in pconf and pconf["focus"]:
372383
w.select_pane(p["pane_id"])
373384

0 commit comments

Comments
 (0)