|
1 | | -# libtmux |
| 1 | +<div align="center"> |
| 2 | + <h1>libtmux</h1> |
| 3 | + <p><strong>Drive tmux from Python: typed, object-oriented control over servers, sessions, windows, and panes.</strong></p> |
| 4 | + <p> |
| 5 | + <a href="https://pypi.org/project/libtmux/"><img src="https://img.shields.io/pypi/v/libtmux.svg" alt="PyPI version"></a> |
| 6 | + <a href="https://libtmux.git-pull.com/"><img src="https://github.com/tmux-python/libtmux/workflows/docs/badge.svg" alt="Docs status"></a> |
| 7 | + <a href="https://github.com/tmux-python/libtmux/actions"><img src="https://github.com/tmux-python/libtmux/workflows/tests/badge.svg" alt="Tests status"></a> |
| 8 | + <a href="https://codecov.io/gh/tmux-python/libtmux"><img src="https://codecov.io/gh/tmux-python/libtmux/branch/master/graph/badge.svg" alt="Coverage"></a> |
| 9 | + <a href="https://github.com/tmux-python/libtmux/blob/master/LICENSE"><img src="https://img.shields.io/github/license/tmux-python/libtmux.svg" alt="License"></a> |
| 10 | + </p> |
| 11 | +</div> |
2 | 12 |
|
3 | | -`libtmux` is a [typed](https://docs.python.org/3/library/typing.html) Python library that provides a wrapper for interacting programmatically with tmux, a terminal multiplexer. You can use it to manage tmux servers, |
4 | | -sessions, windows, and panes. Additionally, `libtmux` powers [tmuxp], a tmux workspace manager. |
| 13 | +--- |
5 | 14 |
|
6 | | -[](https://pypi.org/project/libtmux/) |
7 | | -[](https://libtmux.git-pull.com/) |
8 | | -[](https://github.com/tmux-python/libtmux/actions?query=workflow%3A%22tests%22) |
9 | | -[](https://codecov.io/gh/tmux-python/libtmux) |
10 | | -[](https://github.com/tmux-python/libtmux/blob/master/LICENSE) |
| 15 | +## What is libtmux? |
11 | 16 |
|
12 | | -libtmux builds upon tmux's |
13 | | -[target](http://man.openbsd.org/OpenBSD-5.9/man1/tmux.1#COMMANDS) and |
14 | | -[formats](http://man.openbsd.org/OpenBSD-5.9/man1/tmux.1#FORMATS) to |
15 | | -create an object mapping to traverse, inspect and interact with live |
16 | | -tmux sessions. |
| 17 | +libtmux is a typed Python API over [tmux], the terminal multiplexer. Instead of shelling out and parsing tmux output, you talk to real Python objects: `Server`, `Session`, `Window`, and `Pane`. The same API powers [tmuxp], so it stays battle-tested in real workflows. |
17 | 18 |
|
18 | | -View the [documentation](https://libtmux.git-pull.com/), |
19 | | -[API](https://libtmux.git-pull.com/api.html) information and |
20 | | -[architectural details](https://libtmux.git-pull.com/about.html). |
| 19 | +## Why libtmux? |
21 | 20 |
|
22 | | -# Install |
| 21 | +- Typed, object-oriented control of tmux state |
| 22 | +- Query and traverse live sessions, windows, and panes |
| 23 | +- Raw escape hatch via `.cmd(...)` on any object |
| 24 | +- Works with multiple tmux sockets and servers |
| 25 | +- Context managers for automatic cleanup |
| 26 | +- pytest plugin for isolated tmux fixtures |
| 27 | +- Proven in production via tmuxp and other tooling |
23 | 28 |
|
24 | | -```console |
25 | | -$ pip install --user libtmux |
26 | | -``` |
| 29 | +## Requirements & support |
27 | 30 |
|
28 | | -# Open a tmux session |
| 31 | +- tmux: >= 3.2a |
| 32 | +- Python: >= 3.10 (CPython and PyPy) |
29 | 33 |
|
30 | | -Session name `foo`, window name `bar` |
| 34 | +Maintenance-only backports (no new fixes): |
31 | 35 |
|
32 | | -```console |
33 | | -$ tmux new-session -s foo -n bar |
34 | | -``` |
| 36 | +- Python 2.x: [`v0.8.x`](https://github.com/tmux-python/libtmux/tree/v0.8.x) |
| 37 | +- tmux 1.8-3.1c: [`v0.48.x`](https://github.com/tmux-python/libtmux/tree/v0.48.x) |
35 | 38 |
|
36 | | -# Pilot your tmux session via python |
| 39 | +## Installation |
37 | 40 |
|
38 | | -```console |
39 | | -$ python |
40 | | -``` |
41 | | - |
42 | | -Use [ptpython], [ipython], etc. for a nice shell with autocompletions: |
| 41 | +Stable release: |
43 | 42 |
|
44 | 43 | ```console |
45 | | -$ pip install --user ptpython |
46 | | -``` |
47 | | - |
48 | | -```console |
49 | | -$ ptpython |
50 | | -``` |
51 | | - |
52 | | -Connect to a live tmux session: |
53 | | - |
54 | | -```python |
55 | | ->>> import libtmux |
56 | | ->>> svr = libtmux.Server() |
57 | | ->>> svr |
58 | | -Server(socket_path=/tmp/tmux-.../default) |
59 | | -``` |
60 | | - |
61 | | -Tip: You can also use [tmuxp]'s [`tmuxp shell`] to drop straight into your |
62 | | -current tmux server / session / window pane. |
63 | | - |
64 | | -[tmuxp]: https://tmuxp.git-pull.com/ |
65 | | -[`tmuxp shell`]: https://tmuxp.git-pull.com/cli/shell.html |
66 | | -[ptpython]: https://github.com/prompt-toolkit/ptpython |
67 | | -[ipython]: https://ipython.org/ |
68 | | - |
69 | | -Run any tmux command, respective of context: |
70 | | - |
71 | | -Honors tmux socket name and path: |
72 | | - |
73 | | -```python |
74 | | ->>> server = Server(socket_name='libtmux_doctest') |
75 | | ->>> server.cmd('display-message', 'hello world') |
76 | | -<libtmux...> |
77 | | -``` |
78 | | - |
79 | | -New session: |
80 | | - |
81 | | -```python |
82 | | ->>> server.cmd('new-session', '-d', '-P', '-F#{session_id}').stdout[0] |
83 | | -'$2' |
84 | | -``` |
85 | | - |
86 | | -```python |
87 | | ->>> session.cmd('new-window', '-P').stdout[0] |
88 | | -'libtmux...:2.0' |
89 | | -``` |
90 | | - |
91 | | -From raw command output, to a rich `Window` object (in practice and as shown |
92 | | -later, you'd use `Session.new_window()`): |
93 | | - |
94 | | -```python |
95 | | ->>> Window.from_window_id(window_id=session.cmd('new-window', '-P', '-F#{window_id}').stdout[0], server=session.server) |
96 | | -Window(@2 2:..., Session($1 libtmux_...)) |
97 | | -``` |
98 | | - |
99 | | -Create a pane from a window: |
100 | | - |
101 | | -```python |
102 | | ->>> window.cmd('split-window', '-P', '-F#{pane_id}').stdout[0] |
103 | | -'%2' |
104 | | -``` |
105 | | - |
106 | | -Raw output directly to a `Pane`: |
107 | | - |
108 | | -```python |
109 | | ->>> Pane.from_pane_id(pane_id=window.cmd('split-window', '-P', '-F#{pane_id}').stdout[0], server=window.server) |
110 | | -Pane(%... Window(@1 1:..., Session($1 libtmux_...))) |
111 | | -``` |
112 | | - |
113 | | -List sessions: |
114 | | - |
115 | | -```python |
116 | | ->>> server.sessions |
117 | | -[Session($1 ...), Session($0 ...)] |
| 44 | +pip install --user libtmux |
118 | 45 | ``` |
119 | 46 |
|
120 | | -Filter sessions by attribute: |
| 47 | +Pre-releases (alpha / beta / rc): |
121 | 48 |
|
122 | | -```python |
123 | | ->>> server.sessions.filter(history_limit='2000') |
124 | | -[Session($1 ...), Session($0 ...)] |
125 | | -``` |
126 | | - |
127 | | -Direct lookup: |
128 | | - |
129 | | -```python |
130 | | ->>> server.sessions.get(session_id="$1") |
131 | | -Session($1 ...) |
132 | | -``` |
133 | | - |
134 | | -Filter sessions: |
135 | | - |
136 | | -```python |
137 | | ->>> server.sessions[0].rename_session('foo') |
138 | | -Session($1 foo) |
139 | | ->>> server.sessions.filter(session_name="foo") |
140 | | -[Session($1 foo)] |
141 | | ->>> server.sessions.get(session_name="foo") |
142 | | -Session($1 foo) |
143 | | -``` |
144 | | - |
145 | | -Control your session: |
146 | | - |
147 | | -```python |
148 | | ->>> session |
149 | | -Session($1 ...) |
150 | | - |
151 | | ->>> session.rename_session('my-session') |
152 | | -Session($1 my-session) |
153 | | -``` |
154 | | - |
155 | | -Create new window in the background (don't switch to it): |
156 | | - |
157 | | -```python |
158 | | ->>> bg_window = session.new_window(attach=False, window_name="ha in the bg") |
159 | | ->>> bg_window |
160 | | -Window(@... 2:ha in the bg, Session($1 ...)) |
161 | | - |
162 | | -# Session can search the window |
163 | | ->>> session.windows.filter(window_name__startswith="ha") |
164 | | -[Window(@... 2:ha in the bg, Session($1 ...))] |
165 | | - |
166 | | -# Directly |
167 | | ->>> session.windows.get(window_name__startswith="ha") |
168 | | -Window(@... 2:ha in the bg, Session($1 ...)) |
169 | | - |
170 | | -# Clean up |
171 | | ->>> bg_window.kill() |
| 49 | +```console |
| 50 | +pip install --user --upgrade --pre libtmux |
172 | 51 | ``` |
173 | 52 |
|
174 | | -Close window: |
| 53 | +From the main branch (bleeding edge): |
175 | 54 |
|
176 | | -```python |
177 | | ->>> w = session.active_window |
178 | | ->>> w.kill() |
| 55 | +```console |
| 56 | +pip install --user -e 'git+https://github.com/tmux-python/libtmux.git#egg=libtmux' |
179 | 57 | ``` |
180 | 58 |
|
181 | | -Grab remaining tmux window: |
| 59 | +Tip: libtmux is pre-1.0. Pin a range in projects to avoid surprises: |
182 | 60 |
|
183 | | -```python |
184 | | ->>> window = session.active_window |
185 | | ->>> window.split(attach=False) |
186 | | -Pane(%2 Window(@1 1:... Session($1 ...))) |
| 61 | +```toml |
| 62 | +libtmux = "0.49.*" |
187 | 63 | ``` |
188 | 64 |
|
189 | | -Rename window: |
190 | | - |
191 | | -```python |
192 | | ->>> window.rename_window('libtmuxower') |
193 | | -Window(@1 1:libtmuxower, Session($1 ...)) |
194 | | -``` |
| 65 | +## Quickstart |
195 | 66 |
|
196 | | -Split window (create a new pane): |
| 67 | +Create and drive a tmux session from Python. |
197 | 68 |
|
198 | | -```python |
199 | | ->>> pane = window.split() |
200 | | ->>> pane = window.split(attach=False) |
201 | | ->>> pane.select() |
202 | | -Pane(%3 Window(@1 1:..., Session($1 ...))) |
203 | | ->>> window = session.new_window(attach=False, window_name="test") |
204 | | ->>> window |
205 | | -Window(@2 2:test, Session($1 ...)) |
206 | | ->>> pane = window.split(attach=False) |
207 | | ->>> pane |
208 | | -Pane(%5 Window(@2 2:test, Session($1 ...))) |
| 69 | +```console |
| 70 | +tmux new-session -s demo -n main |
209 | 71 | ``` |
210 | 72 |
|
211 | | -Type inside the pane (send key strokes): |
212 | | - |
213 | 73 | ```python |
214 | | ->>> pane.send_keys('echo hey send now') |
| 74 | +import libtmux |
215 | 75 |
|
216 | | ->>> pane.send_keys('echo hey', enter=False) |
217 | | ->>> pane.enter() |
218 | | -Pane(%1 ...) |
219 | | -``` |
| 76 | +server = libtmux.Server() |
| 77 | +session = server.sessions.get(session_name="demo") |
220 | 78 |
|
221 | | -Grab the output of pane: |
| 79 | +window = session.new_window(attach=False, window_name="workspace") |
| 80 | +left = window.active_pane |
| 81 | +right = window.split_window(vertical=True) |
222 | 82 |
|
223 | | -```python |
224 | | ->>> pane.clear() # clear the pane |
225 | | -Pane(%1 ...) |
226 | | ->>> pane.send_keys("cowsay 'hello'", enter=True) |
227 | | ->>> print('\n'.join(pane.cmd('capture-pane', '-p').stdout)) # doctest: +SKIP |
228 | | -$ cowsay 'hello' |
229 | | - _______ |
230 | | -< hello > |
231 | | - ------- |
232 | | - \ ^__^ |
233 | | - \ (oo)\_______ |
234 | | - (__)\ )\/\ |
235 | | - ||----w | |
236 | | - || || |
237 | | -... |
238 | | -``` |
239 | | - |
240 | | -Traverse and navigate: |
| 83 | +left.send_keys("vim", enter=True) |
| 84 | +right.send_keys("htop", enter=True) |
241 | 85 |
|
242 | | -```python |
243 | | ->>> pane.window |
244 | | -Window(@1 1:..., Session($1 ...)) |
245 | | ->>> pane.window.session |
246 | | -Session($1 ...) |
| 86 | +print(f"Attach with: tmux attach -t {session.name}") |
247 | 87 | ``` |
248 | 88 |
|
249 | | -# Backports |
250 | | - |
251 | | -Unsupported / no security releases or bug fixes: |
252 | | - |
253 | | -- Python 2.x: The backports branch is |
254 | | - [`v0.8.x`](https://github.com/tmux-python/libtmux/tree/v0.8.x). |
255 | | -- tmux 1.8 to 3.1c: The backports branch is |
256 | | - [`v0.48.x`](https://github.com/tmux-python/libtmux/tree/v0.48.x). |
257 | | - |
258 | | -# Donations |
259 | | - |
260 | | -Your donations fund development of new features, testing and support. |
261 | | -Your money will go directly to maintenance and development of the |
262 | | -project. If you are an individual, feel free to give whatever feels |
263 | | -right for the value you get out of the project. |
264 | | - |
265 | | -See donation options at <https://tony.sh/support.html>. |
266 | | - |
267 | | -# Project details |
268 | | - |
269 | | -- tmux support: >= 3.2a |
270 | | -- python support: >= 3.10, pypy, pypy3 |
271 | | -- Source: <https://github.com/tmux-python/libtmux> |
272 | | -- Docs: <https://libtmux.git-pull.com> |
273 | | -- API: <https://libtmux.git-pull.com/api.html> |
274 | | -- Changelog: <https://libtmux.git-pull.com/history.html> |
275 | | -- Issues: <https://github.com/tmux-python/libtmux/issues> |
276 | | -- Test Coverage: <https://codecov.io/gh/tmux-python/libtmux> |
277 | | -- pypi: <https://pypi.python.org/pypi/libtmux> |
278 | | -- Open Hub: <https://www.openhub.net/p/libtmux-python> |
279 | | -- Repology: <https://repology.org/project/python:libtmux/versions> |
280 | | -- License: [MIT](http://opensource.org/licenses/MIT). |
| 89 | +[tmuxp]: https://tmuxp.git-pull.com |
| 90 | +[tmux]: https://github.com/tmux/tmux |
0 commit comments