File: quickstart.md

package info (click to toggle)
python-libtmux 0.47.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,392 kB
  • sloc: python: 7,715; makefile: 199; sh: 21
file content (462 lines) | stat: -rw-r--r-- 11,160 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
(quickstart)=

# Quickstart

libtmux allows for developers and system administrators to control live tmux
sessions using python code.

In this example, we will launch a tmux session and control the windows
from inside a live tmux session.

(requirements)=

## Requirements

- [tmux]
- [pip] - for this handbook's examples

[tmux]: https://tmux.github.io/

(installation)=

## Installation

Next, ensure `libtmux` is installed:

```console
$ pip install --user libtmux
```

(developmental-releases)=

### Developmental releases

New versions of libtmux are published to PyPI as alpha, beta, or release candidates. In their
versions you will see notification like `a1`, `b1`, and `rc1`, respectively. `1.10.0b4` would mean
the 4th beta release of `1.10.0` before general availability.

- [pip]\:

  ```console
  $ pip install --user --upgrade --pre libtmux
  ```

- [pipx]\:

  ```console
  $ pipx install --suffix=@next 'libtmux' --pip-args '\--pre' --force
  // Usage: libtmux@next [command]
  ```

- [uv tool install][uv-tools]\:

  ```console
  $ uv tool install --prerelease=allow libtmux
  ```

- [uv]\:

  ```console
  $ uv add libtmux --prerelease allow
  ```

- [uvx]\:

  ```console
  $ uvx --from 'libtmux' --prerelease allow python
  ```

via trunk (can break easily):

- [pip]\:

  ```console
  $ pip install --user -e git+https://github.com/tmux-python/libtmux.git#egg=libtmux
  ```

- [pipx]\:

  ```console
  $ pipx install --suffix=@master 'libtmux @ git+https://github.com/tmux-python/libtmux.git@master' --force
  ```

- [uv]\:

  ```console
  $ uv tool install libtmux --from git+https://github.com/tmux-python/libtmux.git
  ```

[pip]: https://pip.pypa.io/en/stable/
[pipx]: https://pypa.github.io/pipx/docs/
[uv]: https://docs.astral.sh/uv/
[uv-tools]: https://docs.astral.sh/uv/concepts/tools/
[uvx]: https://docs.astral.sh/uv/guides/tools/
[ptpython]: https://github.com/prompt-toolkit/ptpython

## Start a tmux session

Now, let's open a tmux session.

```console
$ tmux new-session -n bar -s foo
```

This tutorial will be using the session and window name in the example.

Window name `-n`: `bar`
Session name `-s`: `foo`

## Control tmux via python

:::{seealso}

{ref}`api`

:::

```console
$ python
```

For commandline completion, you can also use [ptpython].

```console
$ pip install --user ptpython
```

```console
$ ptpython
```

```{module} libtmux

```

First, we can grab a {class}`Server`.

```python
>>> import libtmux
>>> server = libtmux.Server()
>>> server
Server(socket_path=/tmp/tmux-.../default)
```

:::{tip}

You can also use [tmuxp]'s [`tmuxp shell`] to drop straight into your
current tmux server / session / window pane.

[tmuxp]: https://tmuxp.git-pull.com/
[`tmuxp shell`]: https://tmuxp.git-pull.com/cli/shell.html

:::

:::{note}
You can specify a `socket_name`, `socket_path` and `config_file`
in your server object. `libtmux.Server(socket_name='mysocket')` is
equivalent to `$ tmux -L mysocket`.
:::

`server` is now a living object bound to the tmux server's Sessions,
Windows and Panes.

## Raw, contextual commands

New session:

```python
>>> server.cmd('new-session', '-d', '-P', '-F#{session_id}').stdout[0]
'$2'
```

```python
>>> session.cmd('new-window', '-P').stdout[0]
'libtmux...:2.0'
```

From raw command output, to a rich {class}`Window` object (in practice and as shown
later, you'd use {meth}`Session.new_window()`):

```python
>>> Window.from_window_id(window_id=session.cmd('new-window', '-P', '-F#{window_id}').stdout[0], server=session.server)
Window(@2 2:..., Session($1 libtmux_...))
```

Create a pane from a window:

```python
>>> window.cmd('split-window', '-P', '-F#{pane_id}').stdout[0]
'%2'
```

Raw output directly to a {class}`Pane` (in practice, you'd use {meth}`Window.split()`):

```python
>>> Pane.from_pane_id(pane_id=window.cmd('split-window', '-P', '-F#{pane_id}').stdout[0], server=window.server)
Pane(%... Window(@1 1:..., Session($1 libtmux_...)))
```

## Find your {class}`Session`

If you have multiple tmux sessions open, all methods in {class}`Server` are available.

We can list sessions with {meth}`Server.sessions`:

```python
>>> server.sessions
[Session($1 ...), Session($0 ...)]
```

This returns a list of {class}`Session` objects you can grab. We can
find our current session with:

```python
>>> server.sessions[0]
Session($1 ...)
```

However, this isn't guaranteed, libtmux works against current tmux information, the
session's name could be changed, or another tmux session may be created,
so {meth}`Server.sessions` and {meth}`Server.windows` exist as a lookup.

## Get session by ID

tmux sessions use the `$[0-9]` convention as a way to identify sessions.

`$1` is whatever the ID `sessions()` returned above.

```python
>>> server.sessions.filter(session_id='$1')[0]
Session($1 ...)
```

You may `session = server.get_by_id('$<yourId>')` to use the session object.

## Get session by name / other properties

```python
>>> server.sessions[0].rename_session('foo')
Session($1 foo)

>>> server.sessions.filter(session_name="foo")[0]
Session($1 foo)

>>> server.sessions.get(session_name="foo")
Session($1 foo)
```

With `filter`, pass in attributes and return a list of matches. In
this case, a {class}`Server` holds a collection of child {class}`Session`.
{class}`Session` and {class}`Window` both utilize `filter` to sift
through Windows and Panes, respectively.

So you may now use:

```python
>>> server.sessions[0].rename_session('foo')
Session($1 foo)

>>> session = server.sessions.get(session_name="foo")
>>> session
Session($1 foo)
```

to give us a `session` object to play with.

## Playing with our tmux session

We now have access to `session` from above with all of the methods
available in {class}`Session`.

Let's make a {meth}`Session.new_window`, in the background:

```python
>>> session.new_window(attach=False, window_name="ha in the bg")
Window(@2 ...:ha in the bg, Session($1 ...))
```

So a few things:

1. `attach=False` meant to create a new window, but not to switch to it.
   It is the same as `$ tmux new-window -d`.
2. `window_name` may be specified.
3. Returns the {class}`Window` object created.

:::{note}
Use the API reference {ref}`api` for more commands.
:::

Let's delete that window ({meth}`Session.kill_window`).

Method 1: Use passthrough to tmux's `target` system.

```python
>>> session.kill_window(window.window_id)
```

The window in the bg disappeared. This was the equivalent of
`$ tmux kill-window -t'ha in'`

Internally, tmux uses `target`. Its specific behavior depends on what the
target is, view the tmux manpage for more information:

```
This section contains a list of the commands supported by tmux.  Most commands
accept the optional -t argument with one of target-client, target-session,
target-window, or target-pane.
```

In this case, you can also go back in time and recreate the window again. The CLI
should have history, so navigate up with the arrow key.

```python
>>> session.new_window(attach=False, window_name="ha in the bg")
Window(@2 ...:ha in the bg, Session($1 ...))
```

Try to kill the window by the matching id `@[0-9999]`.

```python
>>> session.new_window(attach=False, window_name="ha in the bg")
Window(@2 ...:ha in the bg, Session($1 ...))

>>> session.kill_window('ha in the bg')
```

In addition, you could also `.kill_window` direction from the {class}`Window`
object:

```python
>>> window = session.new_window(attach=False, window_name="check this out")
>>> window
Window(@2 2:check this out, Session($1 ...))
```

And kill:

```python
>>> window.kill()
```

Use {meth}`Session.windows` and {meth}`Session.windows.filter()` to list and sort
through active {class}`Window`'s.

## Manipulating windows

Now that we know how to create windows, let's use one. Let's use {meth}`Session.active_window()`
to grab our current window.

```python
>>> window = session.active_window
```

`window` now has access to all of the objects inside of {class}`Window`.

Let's create a pane, {meth}`Window.split`:

```python
>>> window.split(attach=False)
Pane(%2 Window(@1 ...:..., Session($1 ...)))
```

Powered up. Let's have a break down:

1. `window = session.active_window()` gave us the {class}`Window` of the current attached to window.
2. `attach=False` assures the cursor didn't switch to the newly created pane.
3. Returned the created {class}`Pane`.

Also, since you are aware of this power, let's commemorate the experience:

```python
>>> window.rename_window('libtmuxower')
Window(@1 ...:..., Session($1 ...))
```

You should have noticed {meth}`Window.rename_window` renamed the window.

## Moving cursor across windows and panes

You have two ways you can move your cursor to new sessions, windows and panes.

For one, arguments such as `attach=False` can be omittted.

```python
>>> pane = window.split()
```

This gives you the {class}`Pane` along with moving the cursor to a new window. You
can also use the `.select_*` available on the object, in this case the pane has
{meth}`Pane.select()`.

```python
>>> pane = window.split(attach=False)
```

```python
>>> pane.select()
Pane(%1 Window(@1 ...:..., Session($1 ...)))
```

```{eval-rst}
.. todo:: create a ``kill_pane()`` method.
```

```{eval-rst}
.. todo:: have a ``.kill()`` and ``.select()`` proxy for Server, Session, Window and Pane objects.
```

## Sending commands to tmux panes remotely

As long as you have the object, or are iterating through a list of them, you can use `.send_keys`.

```python
>>> window = session.new_window(attach=False, window_name="test")
>>> pane = window.split(attach=False)
>>> pane.send_keys('echo hey', enter=False)
```

See the other window, notice that {meth}`Pane.send_keys` has "`echo hey`" written,
_still in the prompt_.

`enter=False` can be used to send keys without pressing return. In this case,
you may leave it to the user to press return himself, or complete a command
using {meth}`Pane.enter()`:

```python
>>> pane.enter()
Pane(%1 ...)
```

### Avoid cluttering shell history

`suppress_history=True` can send commands to pane windows and sessions **without**
them being visible in the history.

```python
>>> pane.send_keys('echo Howdy', enter=True, suppress_history=True)
```

In this case, {meth}`Pane.send_keys` has " `echo Howdy`" written,
automatically sent, the leading space character prevents adding it to the user's
shell history. Omitting `enter=false` means the default behavior (sending the
command) is done, without needing to use `pane.enter()` after.

## Final notes

These objects created use tmux's internal usage of ID's to make servers,
sessions, windows and panes accessible at the object level.

You don't have to see the tmux session to be able to orchestrate it. After
all, {class}`WorkspaceBuilder` uses these same internals to build your
sessions in the background. :)

:::{seealso}

If you want to dig deeper, check out {ref}`API`, the code for
and our [test suite] (see {ref}`development`.)

:::

[workspacebuilder.py]: https://github.com/tmux-python/libtmux/blob/master/libtmux/workspacebuilder.py
[test suite]: https://github.com/tmux-python/libtmux/tree/master/tests
[ptpython]: https://github.com/prompt-toolkit/ptpython