GitHub - tmux-python/libtmux: ⚙️ Python API / wrapper for tmux
🐍 What is libtmux?
libtmux is a typed Python API over tmux, the terminal multiplexer. Stop shelling out and parsing tmux ls. Instead, interact with real Python objects: Server, Session, Window, and Pane. The same API powers tmuxp, so it stays battle-tested in real-world workflows.
✨ Features
- Typed, object-oriented control of tmux state
- Query and traverse live sessions, windows, and panes
- Raw escape hatch via
.cmd(...)on any object - Works with multiple tmux sockets and servers
- Context managers for automatic cleanup
- pytest plugin for isolated tmux fixtures
- Proven in production via tmuxp and other tooling
Requirements & support
- tmux: >= 3.2a
- Python: >= 3.10 (CPython and PyPy)
Maintenance-only backports (no new fixes):
📦 Installation
Stable release:
With pipx:
With uv / uvx:
$ uvx --from "libtmux" pythonFrom the main branch (bleeding edge):
$ pip install 'git+https://github.com/tmux-python/libtmux.git'Tip: libtmux is pre-1.0. Pin a range in projects to avoid surprises:
requirements.txt:
pyproject.toml:
🚀 Quickstart
Open a tmux session
First, start a tmux session to connect to:
$ tmux new-session -s foo -n barPilot your tmux session via Python
Use ptpython, ipython, etc. for a nice REPL with autocompletions:
$ pip install --user ptpythonConnect to a live tmux session:
>>> import libtmux >>> svr = libtmux.Server() >>> svr 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.
Run any tmux command
Every object has a .cmd() escape hatch that honors socket name and path:
>>> server = Server(socket_name='libtmux_doctest') >>> server.cmd('display-message', 'hello world') <libtmux...>
Create a new session:
>>> server.cmd('new-session', '-d', '-P', '-F#{session_id}').stdout[0] '$...'
List and filter sessions
>>> server.sessions [Session($... ...), ...]
Filter by attribute:
>>> server.sessions.filter(history_limit='2000') [Session($... ...), ...]
Direct lookup:
>>> server.sessions.get(session_id=session.session_id) Session($... ...)
Control sessions and windows
Learn more about Workspace Setup
>>> session.rename_session('my-session') Session($... my-session)
Create new window in the background (don't switch to it):
>>> bg_window = session.new_window(attach=False, window_name="bg-work") >>> bg_window Window(@... ...:bg-work, Session($... ...)) >>> session.windows.filter(window_name__startswith="bg") [Window(@... ...:bg-work, Session($... ...))] >>> session.windows.get(window_name__startswith="bg") Window(@... ...:bg-work, Session($... ...)) >>> bg_window.kill()
Split windows and send keys
Learn more about Pane Interaction
>>> pane = window.split(attach=False) >>> pane Pane(%... Window(@... ...:..., Session($... ...)))
Type inside the pane (send keystrokes):
>>> pane.send_keys('echo hello') >>> pane.send_keys('echo hey', enter=False) >>> pane.enter() Pane(%... ...)
Capture pane output
>>> pane.clear() Pane(%... ...) >>> pane.send_keys("echo 'hello world'", enter=True) >>> pane.cmd('capture-pane', '-p').stdout # doctest: +SKIP ["$ echo 'hello world'", 'hello world', '$']
Traverse the hierarchy
Navigate from pane up to window to session:
>>> pane.window Window(@... ...:..., Session($... ...)) >>> pane.window.session Session($... ...)
Core concepts
| libtmux object | tmux concept | Notes |
|---|---|---|
Server |
tmux server / socket | Entry point; owns sessions |
Session |
tmux session ($0, $1,...) |
Owns windows |
Window |
tmux window (@1, @2,...) |
Owns panes |
Pane |
tmux pane (%1, %2,...) |
Where commands run |
Also available: Options and Hooks abstractions for tmux configuration.
Collections are live and queryable:
server = libtmux.Server() session = server.sessions.get(session_name="demo") api_windows = session.windows.filter(window_name__startswith="api") pane = session.active_window.active_pane pane.send_keys("echo 'hello from libtmux'", enter=True)
tmux vs libtmux vs tmuxp
| Tool | Layer | Typical use case |
|---|---|---|
| tmux | CLI / terminal multiplexer | Everyday terminal usage, manual control |
| libtmux | Python API over tmux | Programmatic control, automation, testing |
| tmuxp | App on top of libtmux | Declarative tmux workspaces from YAML / TOML |
Testing & fixtures
Learn more about the pytest plugin
Writing a tool that interacts with tmux? Use our fixtures to keep your tests clean and isolated.
def test_my_tmux_tool(session): # session is a real tmux session in an isolated server window = session.new_window(window_name="test") pane = window.active_pane pane.send_keys("echo 'hello from test'", enter=True) assert window.window_name == "test" # Fixtures handle cleanup automatically
- Fresh tmux server/session/window/pane fixtures per test
- Temporary HOME and tmux config fixtures keep indices stable
TestServerhelper spins up multiple isolated tmux servers
When you might not need libtmux
- Layouts are static and live entirely in tmux config files
- You do not need to introspect or control running tmux from other tools
- Python is unavailable where tmux is running
Project links
Topics: Traversal · Filtering · Pane Interaction · Workspace Setup · Automation Patterns · Context Managers · Options & Hooks
Reference: Docs · API · pytest plugin · Architecture · Changelog · Migration
Project: Issues · Coverage · Releases · License · Support
The Tao of tmux — deep-dive book on tmux fundamentals
Contributing & support
Contributions are welcome. Please open an issue or PR if you find a bug or want to improve the API or docs. If libtmux helps you ship, consider sponsoring development via support.