NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1606 most downloaded on PyPI
Typed library that provides an ORM wrapper for tmux, a terminal multiplexer.
Last release 2 months ago
12 Jul 2026
Release timing varies
gaps range from 8 days to 5 months
Nearly every release is documented
notes for 58 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
10 years old
166 releases · first in 2016
…and index instead of an arbitrary one. One breaking change lands too — query errors now belong to the LibTmuxException hierarchy (see below).
libtmux 0.62.0 teaches libtmux objects to locate themselves and to resolve shared windows the way tmux does. Code running inside tmux can now find the object it's running in, a window can name every session that holds it, and point lookups follow tmux's own choice of session and index instead of an arbitrary one. One breaking change lands too — query errors now belong to the LibTmuxException hierarchy (see below).
from_env()Most libtmux code starts from a handle you already hold. But a script in a split, a tmux hook, a test harness, or an agent is running inside a pane and holds nothing. tmux already wrote the answer into the pane's environment, and each level of the hierarchy now reads it back:
from libtmux import Pane
pane = Pane.from_env() # the pane this code is running in
window = pane.window # …and you're back on the hierarchy from there
session = window.session
Server.from_env(), Session.from_env(), and Window.from_env() do the same for the rest of the tree. Each also accepts an environment mapping in place of os.environ, so code that locates itself stays testable outside a pane. Outside tmux — or with an environment that doesn't parse — the family raises libtmux.exc.NotInsideTmux rather than guessing.
The answer stays true as tmux moves things around: it survives a move-window, and for a window linked into several sessions it names the session tmux itself would act on. See the Locating yourself guide for the whole story.
Window.linked_sessionsA window can be linked into more than one session at once. Window.linked_sessions lists every Session from which a window is reachable — a typical window returns one, a linked or grouped window returns several, with each session appearing once even when it holds the window at multiple indexes. Use it when you need every holder; Window.session still follows the single session_id recorded on that instance.
Window.from_window_id(), Pane.from_pane_id(), Window.refresh(), and Pane.refresh() used to pick one holding session by accident — whichever sorted last by name — so their answer could change when you renamed a session. They now let tmux resolve the object, reporting the session tmux itself would act on, and give Window.window_index the index tmux would choose for a window linked twice. For the ordinary single-session window, nothing changes; the lookups also got cheaper. See winlinks.
QueryList.get() now names both the lookup and its outcome. A missing pane_id="%99" raises ObjectDoesNotExist with No objects found: pane_id='%99'; two matches for pane_id="%0" raise MultipleObjectsReturned with Multiple objects returned (2): pane_id='%0'.
LibTmuxException hierarchylibtmux.exc.ObjectDoesNotExist and libtmux.exc.MultipleObjectsReturned now subclass libtmux.exc.LibTmuxException. Because TmuxObjectDoesNotExist inherits from ObjectDoesNotExist, it now falls under LibTmuxException too.
If you catch both families, order the handlers from most specific to most general so the broad clause no longer intercepts lookup outcomes:
from libtmux import exc
try:
pane = server.panes.get(pane_id="%0")
except exc.ObjectDoesNotExist: # specific first
pane = None
except exc.LibTmuxException: # general last
pane = None
Blanket retry policies for LibTmuxException should now exclude ObjectDoesNotExist and MultipleObjectsReturned — they report deterministic missing or ambiguous lookups, not transient tmux failures. See the migration notes for a retry predicate example.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.61.0...v0.62.0
One column per quarter.
libtmux 0.62.0 teaches libtmux objects to locate themselves and to resolve
shared windows the way tmux does. Code running inside tmux can now find its
own pane, window, session, and server with
{meth}Pane.from_env() <libtmux.Pane.from_env> and its siblings, and a window
reports every session that holds it through
{attr}~libtmux.Window.linked_sessions. Point lookups now resolve a linked
window to the session and index tmux itself would act on rather than an
arbitrary one. The release also folds {exc}~libtmux.exc.ObjectDoesNotExist
and {exc}~libtmux.exc.MultipleObjectsReturned into the
{exc}~libtmux.exc.LibTmuxException hierarchy — a breaking change for
exception handlers, detailed below. See {ref}self-location and
{ref}winlinks for the full story.
{exc}~libtmux.exc.ObjectDoesNotExist and
{exc}~libtmux.exc.MultipleObjectsReturned now subclass
{exc}~libtmux.exc.LibTmuxException. Because
{exc}~libtmux.exc.TmuxObjectDoesNotExist inherits from
{exc}~libtmux.exc.ObjectDoesNotExist, it now falls under
{exc}~libtmux.exc.LibTmuxException as well. Previously, a lookup-specific
handler could follow the common libtmux handler:
from libtmux import exc
try:
pane = server.panes.get(pane_id="%0")
except exc.LibTmuxException:
pane = None
except exc.ObjectDoesNotExist:
pane = None
Put the more specific handler first:
from libtmux import exc
try:
pane = server.panes.get(pane_id="%0")
except exc.ObjectDoesNotExist:
pane = None
except exc.LibTmuxException:
pane = None
Blanket retry policies for {exc}~libtmux.exc.LibTmuxException should exclude
{exc}~libtmux.exc.ObjectDoesNotExist and
{exc}~libtmux.exc.MultipleObjectsReturned: they report deterministic missing
or ambiguous lookups, not transient tmux failures.
Code running inside tmux — a script in a split, a hook, a test harness, an
agent — can now ask libtmux where it is.
{meth}Pane.from_env() <libtmux.Pane.from_env> returns the pane the calling
process is running in, and {meth}Server.from_env() <libtmux.Server.from_env>,
{meth}Session.from_env() <libtmux.Session.from_env> and
{meth}Window.from_env() <libtmux.Window.from_env> do the same for the rest of
the hierarchy. Each takes an optional environment mapping, so code that locates
itself stays testable outside a pane.
The answer stays true as tmux moves things around: it survives a move-window,
and for a window linked into several sessions it names the session tmux itself
would act on. Outside tmux, or with an environment that does not parse, the
family raises {exc}~libtmux.exc.NotInsideTmux rather than guessing.
See {ref}self-location for the whole story — why the session id tmux exports
goes stale, the window that contains you versus the one in front of you, and
how to test code that locates itself.
{attr}~libtmux.Window.linked_sessions lists every
{class}~libtmux.Session from which a window is reachable. A typical window
returns one session; a linked or grouped window can return several, with each
session appearing once even when it holds the same window at multiple indexes.
Use {attr}~libtmux.Window.linked_sessions when you need every holder.
{attr}~libtmux.Window.session follows the session_id recorded on that
{class}~libtmux.Window instance; use
{meth}~libtmux.Window.from_window_id when you need tmux to resolve a window id
afresh.
See {ref}winlinks for the relationship between sessions, indexes, and shared
windows.
A window can be linked into several sessions at once, and libtmux used to pick
one of them by accident — whichever sorted last by name. So
{meth}Window.from_window_id() <libtmux.Window.from_window_id>,
{meth}Pane.from_pane_id() <libtmux.Pane.from_pane_id>,
{meth}Window.refresh() <libtmux.Window.refresh> and
{meth}Pane.refresh() <libtmux.Pane.refresh> could change their answer when you
renamed a session.
libtmux now lets tmux resolve the object, so a window reports the session tmux itself would act on. For a window in a single session — the ordinary case — nothing changes. For a linked or grouped window, note that tmux picks by activity: the session a window reports can change as focus moves between the sessions holding it, where before it was stable but arbitrary. The lookups also got cheaper: they list one window's panes or one session's windows instead of scanning the whole server.
tmux lets one session link the same window at multiple indexes.
{meth}Window.from_window_id() <libtmux.Window.from_window_id> and
{meth}Window.refresh() <libtmux.Window.refresh> now give
{attr}Window.window_index <libtmux.Window.window_index> the index tmux would
choose: the current link when that window is current, otherwise the
lowest-indexed link. Windows held at one index are unaffected.
{meth}QueryList.get() <libtmux._internal.query_list.QueryList.get> now names
both the lookup and its missing or ambiguous outcome. A missing
pane_id="%99" raises {exc}~libtmux.exc.ObjectDoesNotExist with
No objects found: pane_id='%99'; two matches for pane_id="%0" raise
{exc}~libtmux.exc.MultipleObjectsReturned with
Multiple objects returned (2): pane_id='%0'.
{ref}winlinks explains that
{attr}Server.windows <libtmux.Server.windows> and
{attr}Server.panes <libtmux.Server.panes> enumerate {term}winlinks <winlink>
— the session, index, and window relationships tmux stores — rather than
deduplicating windows. A shared window can therefore appear once for each path
by which a session reaches it.
Read those rows when you need their session and index context. For a unique
lookup by id, use {meth}Pane.from_pane_id() <libtmux.Pane.from_pane_id> or
{meth}Window.from_window_id() <libtmux.Window.from_window_id>; use
{attr}~libtmux.Window.linked_sessions when you need every holding session.
libtmux 0.62.0 teaches libtmux objects to locate themselves and to resolve shared windows the way tmux does. Code running inside tmux can now find its own pane, window, session, and server with Pane.from_env() and its siblings, and a window reports every session that holds it through linked_sessions . Point lookups now resolve a linked window to the session and index tmux itself would act on rather than an arbitrary one. The release also folds ObjectDoesNotExist and MultipleObjectsReturned into the LibTmuxException hierarchy — a breaking change for exception handlers, detailed below. See Locating yourself and When one window is in two sessions for the full story.
ObjectDoesNotExist and MultipleObjectsReturned now subclass LibTmuxException . Because TmuxObjectDoesNotExist inherits from ObjectDoesNotExist , it now falls under LibTmuxException as well. Previously, a lookup-specific handler could follow the common libtmux handler:
from libtmux import exc try : pane = server . panes . get ( pane_id = "%0" ) except exc . LibTmuxException : pane = None except exc . ObjectDoesNotExist : pane = None
Put the more specific handler first:
from libtmux import exc try : pane = server . panes . get ( pane_id = "%0" ) except exc . ObjectDoesNotExist : pane = None except exc . LibTmuxException : pane = None
Blanket retry policies for LibTmuxException should exclude ObjectDoesNotExist and MultipleObjectsReturned : they report deterministic missing or ambiguous lookups, not transient tmux failures.
Code running inside tmux — a script in a split, a hook, a test harness, an agent — can now ask libtmux where it is. Pane.from_env() returns the pane the calling process is running in, and Server.from_env() , Session.from_env() and Window.from_env() do the same for the rest of the hierarchy. Each takes an optional environment mapping, so code that locates itself stays testable outside a pane.
The answer stays true as tmux moves things around: it survives a move-window , and for a window linked into several sessions it names the session tmux itself would act on. Outside tmux, or with an environment that does not parse, the family raises NotInsideTmux rather than guessing.
See Locating yourself for the whole story — why the session id tmux exports goes stale, the window that contains you versus the one in front of you, and how to test code that locates itself.
linked_sessions lists every Session from which a window is reachable. A typical window returns one session; a linked or grouped window can return several, with each session appearing once even when it holds the same window at multiple indexes.
Use linked_sessions when you need every holder. session follows the session_id recorded on that Window instance; use from_window_id() when you need tmux to resolve a window id afresh. See When one window is in two sessions for the relationship between sessions, indexes, and shared windows.
A window can be linked into several sessions at once, and libtmux used to pick one of them by accident — whichever sorted last by name. So Window.from_window_id() , Pane.from_pane_id() , Window.refresh() and Pane.refresh() could change their answer when you renamed a session.
libtmux now lets tmux resolve the object, so a window reports the session tmux itself would act on. For a window in a single session — the ordinary case — nothing changes. For a linked or grouped window, note that tmux picks by activity : the session a window reports can change as focus moves between the sessions holding it, where before it was stable but arbitrary. The lookups also got cheaper: they list one window’s panes or one session’s windows instead of scanning the whole server.
tmux lets one session link the same window at multiple indexes. Window.from_window_id() and Window.refresh() now give Window.window_index the index tmux would choose: the current link when that window is current, otherwise the lowest-indexed link. Windows held at one index are unaffected.
QueryList.get() now names both the lookup and its missing or ambiguous outcome. A missing pane_id="%99" raises ObjectDoesNotExist with No objects found: pane_id='%99' ; two matches for pane_id="%0" raise MultipleObjectsReturned with Multiple objects returned (2): pane_id='%0' .
When one window is in two sessions explains that Server.windows and Server.panes enumerate winlinks — the session, index, and window relationships tmux stores — rather than deduplicating windows. A shared window can therefore appear once for each path by which a session reaches it.
Read those rows when you need their session and index context. For a unique lookup by id, use Pane.from_pane_id() or Window.from_window_id() ; use linked_sessions when you need every holding session.
libtmux 0.61.0 hardens support for the tmux 3.7 patch line. It fixes Pane.break_pane() naming broken-out windows libtmux instead of tmux's own default
libtmux 0.61.0 hardens support for the tmux 3.7 patch line. It fixes Pane.break_pane() naming broken-out windows libtmux instead of tmux's own default on tmux 3.7a/3.7b, and adds get_version_str() for reading the raw tmux version with its point-release suffix intact.
break_pane() keeps tmux's default window name on 3.7a/3.7b (#699). Breaking a pane into a new window without an explicit window_name left it named libtmux on tmux 3.7a/3.7b; it now keeps tmux's own default (typically the running command). Passing window_name is unaffected.get_version_str() (#699). libtmux.common.get_version_str() returns the running tmux version verbatim, keeping the point-release suffix ("3.7a") that get_version() strips for numeric comparison — useful for telling patch releases apart, e.g. 3.7 from 3.7a.Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.60.0...v0.61.0
libtmux 0.61.0 hardens support for the tmux 3.7 patch line. It fixes
{meth}~libtmux.Pane.break_pane naming broken-out windows libtmux
instead of tmux's own default on tmux 3.7a/3.7b, and adds
{func}~libtmux.common.get_version_str for the raw tmux version string
with its point-release suffix intact. The test suite now also passes
against tmux 3.7a and 3.7b.
get_version_str() (#699){func}libtmux.common.get_version_str returns the running tmux version
verbatim, keeping the point-release suffix ("3.7a") that
{func}~libtmux.common.get_version strips for numeric comparison. Reach
for it to distinguish patch releases whose behavior differs -- for
example telling tmux 3.7 from 3.7a.
break_pane() keeps tmux's default window name on 3.7a/3.7b (#699)On tmux 3.7a/3.7b, breaking a pane into a new window without an explicit
window_name left it named libtmux instead of tmux's own default
(typically the running command). {meth}~libtmux.Pane.break_pane now
keeps that natural name; passing window_name is unaffected.
libtmux's test suite now passes against tmux 3.7a and 3.7b, which CI also
exercises. A window-name test had assumed the :/. rejection tmux
introduced in 3.7, but tmux reverted it in 3.7a as "overly pernickety" — so
the test now checks these names are accepted.
Purely additive — no breaking changes. New 3.7 parameters are gated on tmux's reported version; unsupported flags warn and are ignored on older tmux,…
libtmux 0.60.0 completes tmux 3.7 feature parity. Building on the 3.7 compatibility shipped in 0.59.0, it adds first-class floating panes via Pane.new_pane() and Window.new_pane(), types tmux 3.7's new server, session, window, and pane options, exposes the new pane format variables on Pane, and wraps tmux 3.7's new command flags. Every 3.7-only surface is version-gated, so tmux 3.2a–3.6 keep working unchanged.
new-pane) (#694)tmux 3.7's flagship feature is now reachable through Window.new_pane() and Pane.new_pane(). A floating pane sits above the tiled layout like a popup but behaves like a real pane. Pass width/height to size it and x/y to position it, alongside the usual shell, start_directory, environment, zoom, and empty, plus style, active_border_style, inactive_border_style, message, and keep (remain-on-exit). The returned Pane reports pane_floating_flag == "1". Requires tmux 3.7+; see the floating panes guide.
The option dataclasses now type tmux 3.7's new options. On the server, get-clipboard; on the session, focus-follows-mouse, message-format, and prompt-command-cursor-style; and on the window, the copy-mode line-number options, the tree-mode preview options, and the window-pane status formats (of these, tree-mode-preview-format is also pane-scoped). The pane remain-on-exit option gained tmux 3.7's key value, and pane-active-border-style / pane-border-style (widened to pane scope in 3.7) now type on the pane too.
Pane now exposes tmux 3.7's new pane format variables: the floating-pane geometry and flags (pane_floating_flag, pane_x, pane_y, pane_z, pane_flags, pane_zoomed_flag), the OSC 9;4 progress report (pane_pb_progress, pane_pb_state), pane_pipe_pid, and the bracket_paste_flag and synchronized_output_flag screen-mode flags. Each is version-gated, so the format template stays clean on tmux 3.2a–3.6.
New tmux 3.7 flags are exposed on existing wrappers: Pane.capture_pane() gains hyperlinks (-H), line_numbers (-L), and line_flags (-F); Window.split() / Pane.split() gain empty (-E) plus style/active_border_style/inactive_border_style (-s/-S/-R), message (-m), and keep (-k); Session.kill() gains group (-g); and Pane.paste_buffer() gains no_vis (-S). On the server, Server.command_prompt() gains no_freeze (-C), Server.list_keys() gains format_ (-F), Server.run_shell() accepts trailing args expanded as #{1}/#{2}, and Server.refresh_client() gains request_clipboard (-l). Each warns and is ignored on tmux < 3.7.
Purely additive — no breaking changes. New 3.7 parameters are gated on tmux's reported version; unsupported flags warn and are ignored on older tmux, and floating-pane creation raises there because new-pane does not exist before 3.7. tmux 3.2a–3.6 remain supported and tested.
Links: Changelog · API docs · Floating panes guide · PyPI
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.59.0...v0.60.0
libtmux 0.60.0 completes tmux 3.7 feature parity. Building on the 3.7
compatibility shipped in 0.59.0, it adds first-class floating panes via
{meth}~libtmux.Pane.new_pane and {meth}~libtmux.Window.new_pane,
types tmux 3.7's new server, session, window, and pane options, exposes
the new pane format variables on {class}~libtmux.Pane, and wraps tmux
3.7's new command flags. Every 3.7-only surface is version-gated, so
tmux 3.2a-3.6 keep working unchanged. See {ref}floating-panes for a
guide.
The option dataclasses now type tmux 3.7's new options. On the server,
get-clipboard; on the session, focus-follows-mouse,
message-format and prompt-command-cursor-style; and on the window,
the copy-mode line-number options, the tree-mode preview options and the
window-pane status formats (of these, tree-mode-preview-format is also
pane-scoped). The pane remain-on-exit option gained tmux 3.7's key
value, and pane-active-border-style / pane-border-style (widened to
pane scope in 3.7) now type on the pane too.
{class}~libtmux.Pane now exposes tmux 3.7's new pane format
variables: the floating-pane geometry and flags (pane_floating_flag,
pane_x, pane_y, pane_z, pane_flags, pane_zoomed_flag), the
OSC 9;4 progress report (pane_pb_progress, pane_pb_state),
pane_pipe_pid, and the bracket_paste_flag and
synchronized_output_flag screen-mode flags. Each is version-gated, so
the format template stays clean on tmux 3.2a–3.6.
New tmux 3.7 flags are exposed on existing wrappers:
{meth}~libtmux.Pane.capture_pane gains hyperlinks (-H, hyperlink
targets only),
line_numbers (-L) and line_flags (-F);
{meth}~libtmux.Window.split / {meth}~libtmux.Pane.split gain empty
(-E, an empty pane) plus style/active_border_style/
inactive_border_style (-s/-S/-R), message (-m) and keep
(-k, remain-on-exit); {meth}~libtmux.Session.kill gains group
(-g, kill the session group); and {meth}~libtmux.Pane.paste_buffer
gains no_vis (-S, paste raw bytes). On the server,
{meth}~libtmux.Server.command_prompt gains no_freeze (-C, don't
freeze panes), {meth}~libtmux.Server.list_keys gains format_
(-F, custom output format), {meth}~libtmux.Server.run_shell
accepts trailing args expanded as #{1}/#{2}, and
{meth}~libtmux.Server.refresh_client gains request_clipboard
(-l, request the terminal clipboard). Each warns and is ignored on
tmux < 3.7.
new-pane) (#694)tmux 3.7's flagship feature, floating panes, is now reachable through
{meth}Window.new_pane() <libtmux.Window.new_pane> and
{meth}Pane.new_pane() <libtmux.Pane.new_pane>. A
floating pane sits above the tiled layout like a popup but behaves like
a real pane. Pass width/height to size it and x/y to position
it, alongside the usual shell, start_directory, environment,
zoom and empty, plus style, active_border_style,
inactive_border_style, message and keep (remain-on-exit). The
returned {class}~libtmux.Pane reports
pane_floating_flag == "1". Requires tmux 3.7+. See {ref}floating-panes
for a guide.
libtmux 0.60.0 completes tmux 3.7 feature parity. Building on the 3.7 compatibility shipped in 0.59.0, it adds first-class floating panes via new_pane() and new_pane() , types tmux 3.7’s new server, session, window, and pane options, exposes the new pane format variables on Pane , and wraps tmux 3.7’s new command flags. Every 3.7-only surface is version-gated, so tmux 3.2a-3.6 keep working unchanged. See Floating panes for a guide.
The option dataclasses now type tmux 3.7’s new options. On the server, get-clipboard ; on the session, focus-follows-mouse , message-format and prompt-command-cursor-style ; and on the window, the copy-mode line-number options, the tree-mode preview options and the window-pane status formats (of these, tree-mode-preview-format is also pane-scoped). The pane remain-on-exit option gained tmux 3.7’s key value, and pane-active-border-style / pane-border-style (widened to pane scope in 3.7) now type on the pane too.
Pane now exposes tmux 3.7’s new pane format variables: the floating-pane geometry and flags ( pane_floating_flag , pane_x , pane_y , pane_z , pane_flags , pane_zoomed_flag ), the OSC 9;4 progress report ( pane_pb_progress , pane_pb_state ), pane_pipe_pid , and the bracket_paste_flag and synchronized_output_flag screen-mode flags. Each is version-gated, so the format template stays clean on tmux 3.2a–3.6.
New tmux 3.7 flags are exposed on existing wrappers: capture_pane() gains hyperlinks ( -H , hyperlink targets only), line_numbers ( -L ) and line_flags ( -F ); split() / split() gain empty ( -E , an empty pane) plus style / active_border_style / inactive_border_style ( -s / -S / -R ), message ( -m ) and keep ( -k , remain-on-exit); kill() gains group ( -g , kill the session group); and paste_buffer() gains no_vis ( -S , paste raw bytes). On the server, command_prompt() gains no_freeze ( -C , don’t freeze panes), list_keys() gains format_ ( -F , custom output format), run_shell() accepts trailing args expanded as #{1} / #{2} , and refresh_client() gains request_clipboard ( -l , request the terminal clipboard). Each warns and is ignored on tmux < 3.7.
tmux 3.7’s flagship feature, floating panes, is now reachable through Window.new_pane() and Pane.new_pane() . A floating pane sits above the tiled layout like a popup but behaves like a real pane. Pass width / height to size it and x / y to position it, alongside the usual shell , start_directory , environment , zoom and empty , plus style , active_border_style , inactive_border_style , message and keep (remain-on-exit). The returned Pane reports pane_floating_flag == "1" . Requires tmux 3.7+. See Floating panes for a guide.
Restore compatibility with tmux 3.7 by @tony in https://github.com/tmux-python/libtmux/pull/693
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.58.1...v0.59.0
libtmux 0.59.0 adds support for tmux 3.7. tmux 3.7 shipped an upstream
regression that crashed the server on break-pane;
{meth}~libtmux.Pane.break_pane now works around it so breaking and
rejoining panes works again, and tmux 3.7 joins the tested version
matrix alongside 3.2a-3.6.
break-pane no longer crashes the tmux 3.7 server (#693)tmux 3.7 shipped a regression where break-pane aborts the server with
a NULL-pointer dereference whenever a pane is broken out of a window
without an explicit name (fixed upstream after the 3.7 release).
{meth}~libtmux.Pane.break_pane now works around this on tmux 3.7 by
always supplying a name and applying the requested one afterwards, so
breaking a pane into a new window — and joining it back with
{meth}~libtmux.Pane.join — work again. Older and post-3.7 tmux are
unaffected.
This is a bug-fix-only patch release, per libtmux's pre-1.0 version policy (patch = bug fixes; minor = features/breaking changes).
libtmux 0.58.1 restores compatibility with pytest 9.1. libtmux's bundled pytest plugin no longer aborts at import time, so projects that rely on libtmux's fixtures can move to the latest pytest without their test suite failing before collection.
pytest 9.1 rejects marks applied to fixture functions during plugin import — before collection begins. Because the plugin ships as an installed entry point, this aborted the entire test session for any downstream project that has libtmux installed (for example, tmuxp). The fix removes a no-op skipif mark from the zshrc fixture; the real zsh handling lives in conftest.py and is unchanged, so behavior is identical for zsh and non-zsh users.
This is a bug-fix-only patch release, per libtmux's pre-1.0 version policy (patch = bug fixes; minor = features/breaking changes).
See the CHANGES entry for full details.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.58.0...v0.58.1
libtmux 0.58.1 restores compatibility with pytest 9.1. The bundled pytest plugin no longer aborts at import time, so projects that rely on libtmux's fixtures can move to the latest pytest without their test suite failing before collection.
libtmux's pytest plugin (see {ref}pytest_plugin) now imports cleanly
under pytest 9.1, which began rejecting marks applied to fixture
functions. Projects that use libtmux's fixtures can upgrade to pytest
9.1+ without the plugin aborting before test collection.
libtmux 0.58.0 fixes subprocess output decoding on non-UTF-8 locales. Both tmux_cmd and ControlMode now enforce UTF-8 when reading tmux output, matchi
libtmux 0.58.0 fixes subprocess output decoding on non-UTF-8 locales. Both tmux_cmd and ControlMode now enforce UTF-8 when reading tmux output, matching tmux's own encoding contract.
tmux_cmd and ControlMode now pass encoding="utf-8" to subprocess.Popen, ensuring tmux output is decoded correctly regardless of the system locale. Previously, on non-UTF-8 locales, the FORMAT_SEPARATOR character (U+241E) was corrupted during decoding, causing list accessors (Server.sessions, etc.) to return empty results.
Links: Changelog · API docs · PyPI
Full Changelog: v0.57.1...v0.58.0
libtmux 0.58.0 fixes subprocess output decoding on non-UTF-8 locales.
Both {class}~libtmux.common.tmux_cmd and
{class}~libtmux._internal.control_mode.ControlMode now enforce UTF-8
when reading tmux output, matching tmux's own encoding contract.
{class}~libtmux.common.tmux_cmd and {class}~libtmux._internal.control_mode.ControlMode
now pass encoding="utf-8" to subprocess.Popen, ensuring tmux output
is decoded correctly regardless of the system locale. Previously, on
non-UTF-8 locales, the {data}~libtmux.formats.FORMAT_SEPARATOR character
(U+241E) was corrupted during decoding, causing list accessors
({attr}~libtmux.Server.sessions, etc.) to return empty results.
libtmux 0.57.1 restores the lenient-by-default behavior for Server.sessions and Server.clients . Both accessors return an empty QueryList on tmux fail
libtmux 0.57.1 restores the lenient-by-default behavior for Server.sessions and Server.clients. Both accessors return an empty QueryList on tmux failure (socket permission errors, subprocess crashes, missing daemon) again,
matching the contract Server.sessions has carried since 0.17.0.
Code written for 0.57.0 with try/except LibTmuxException around these accessors keeps working — the except clause just becomes unreachable. For an explicit connectivity signal, use Server.is_alive() or Server.raise_if_dead().
Server.search_sessions, Server.search_windows, and Server.search_panes continue to raise — they take a caller-supplied filter where a tmux error carries meaningful signal.
See the CHANGES entry for full details.
Full Changelog: v0.57.0...v0.57.1
Restores the "lenient-by-default" behavior for
{attr}~libtmux.Server.sessions and {attr}~libtmux.Server.clients that
was changed in 0.57.0.
Server.sessions and Server.clients accessors{attr}~libtmux.Server.sessions and {attr}~libtmux.Server.clients once
again return an empty {class}~libtmux._internal.query_list.QueryList
when tmux fails for any reason (permission errors, subprocess crashes,
etc.).
This reverts a change in 0.57.0 that made these accessors raise
{exc}~libtmux.exc.LibTmuxException. The restored behavior matches the
contract {attr}~libtmux.Server.sessions has followed since 0.17.0.
try/except LibTmuxException blocks around
these accessors for 0.57.0 can now remove them.~libtmux.Server.is_alive or
{meth}~libtmux.Server.raise_if_dead methods.search_* methodsThe newer {meth}~libtmux.Server.search_sessions,
{meth}~libtmux.Server.search_windows, and
{meth}~libtmux.Server.search_panes continue to raise on tmux errors.
Since these methods take a caller-supplied filter, a tmux failure is
considered a meaningful error signal that should not be swallowed.
Restores the “lenient-by-default” behavior for sessions and clients that was changed in 0.57.0.
sessions and clients once again return an empty QueryList when tmux fails for any reason (permission errors, subprocess crashes, etc.).
This reverts a change in 0.57.0 that made these accessors raise LibTmuxException . The restored behavior matches the contract sessions has followed since 0.17.0.
Upgrade path: Code that added try/except LibTmuxException blocks around these accessors for 0.57.0 can now remove them.
Connectivity checks: If you need to distinguish between “no results” and “tmux is unreachable”, use the explicit is_alive() or raise_if_dead() methods.
The newer search_sessions() , search_windows() , and search_panes() continue to raise on tmux errors. Since these methods take a caller-supplied filter, a tmux failure is considered a meaningful error signal that should not be swallowed.
libtmux 0.57.0 broadens tmux support around attached clients, tmux-native filtering, and format-token fields. Client gives callers a typed object for
libtmux 0.57.0 broadens tmux support around attached clients, tmux-native filtering, and format-token fields. Client gives callers a typed object for attached terminals, search_*() methods let tmux return only matching sessions, windows, and panes, and more tmux format tokens are exposed as typed attributes. LibTmuxException now records which tmux subcommand failed, making command errors easier to handle downstream.
Full release notes: https://libtmux.git-pull.com/history.html#libtmux-0-57-0-2026-05-17
LibTmuxException string form gains a subcommand prefixWhen LibTmuxException is raised from a libtmux method, str(exc) now begins with the originating tmux subcommand name followed by ": ". An error from Session.last_window() used to render as "can't find window" and now renders as "last-window: can't find window".
exc.args[0] still carries the original tmux error text, and the new LibTmuxException.subcommand attribute exposes the tmux subcommand name as a separate field.
The error-payload type also changed: exc.args[0] is now str, joined with "\n" when tmux emitted multiple stderr lines. Previously it was list[str]. Code that pattern-matches on str(exc) exactly, anchors a regex with ^ against the old shape, or indexes exc.args[0] element-by-element will no longer match. Substring matches ("can't find" in str(exc)) and unanchored re.search patterns continue to work unchanged.
See the migration guide for the upgrade pattern.
Server.sessions, Server.clients, Server.search_sessions raise on tmux errorsPreviously, a tmux command failure under these accessors could return an empty QueryList indistinguishable from "no sessions / no clients attached" or "filter matched nothing". They now let LibTmuxException propagate for real tmux failures.
Genuine empty results — a server with no attached clients, or a filter that matched zero sessions — still return an empty QueryList. A missing or not-yet-started tmux server is also still treated as an empty result, preserving the historic contract that a fresh Server can be safely introspected before its daemon is up. Other tmux errors, such as socket permission failures or unsupported flags, now surface.
Client object and Server.clients accessor. New typed dataclass for tmux's attached-client model. client_name is the stable identifier; attached_session / attached_window / attached_pane re-read list-clients before resolving. attached_pane follows the attached session's current window — that can differ from the per-client active pane set by select-pane -P.Server.display_message and Window.display_message. Server reads like #{version} and #{socket_path} work without a pane handle; window reads (#{window_zoomed_flag}, #{window_active_clients_list}) auto-bind to the window's id. All three display_message wrappers (including the existing Pane.display_message) surface tmux stderr via warnings.warn rather than dropping it silently.search_*(). Server.search_panes / search_windows / search_sessions plus the Session and Window analogues take a filter= kwarg routed to tmux's -f flag. tmux evaluates the expression and returns only matching objects. Caveat: tmux silently expands a malformed expression to empty, which it treats as false — a typo looks identical to "no matches".Pane.send_keys(cmd=None, …) flag-only invocation. Pass cmd=None together with reset=True or repeat=N to invoke tmux's flag-only send-keys -R / send-keys -N <n> form without any trailing key argument.Server.list_buffers(format_string=, filter=). Project a chosen -F template or push a buffer-name match expression through tmux's format engine — same bad-filter caveat as the search_* methods.Server.run_shell(cwd=, show_stderr=). New kwargs map to tmux's -c (3.4+) and -E (3.6+) flags. Older tmux warns and ignores the kwarg instead of erroring.Pane.capture_pane(pending=True). Return bytes tmux has read from the pane but not yet committed to the terminal — useful for diagnosing programs whose output stalls mid-sequence.Pane / Window / Session / Client expose more typed attributes for pane state (pane_dead, pane_in_mode, pane_marked, pane_synchronized, pane_path, pane_pipe), window state (window_zoomed_flag, window_silence_flag, window_flags), session state (session_marked), and client state (client_session, client_readonly, client_termtype). Tokens the running tmux does not support stay None instead of making the listing fail.Pane.reset() now clears pane scrollback. In 0.56.0 the history clear silently no-op'd, leaving the scrollback intact (#650).libtmux.client.session.pane_id reflects the active pane in the session's current window.Full Changelog: v0.56.0...v0.57.0
libtmux on master [?] ❯ cat release.md
libtmux 0.57.0 broadens tmux support around attached clients, tmux-native filtering, and format-token fields. Client gives callers a typed object for attached terminals, search_*() methods let tmux return only matching sessions, windows, and panes, and more tmux format tokens are exposed as typed attributes. LibTmuxException now records which tmux subcommand failed, making command errors easier to handle downstream.
Full release notes: https://libtmux.git-pull.com/history/#libtmux-0-57-0-2026-05-17
LibTmuxException string form gains a subcommand prefixWhen LibTmuxException is raised from a libtmux method, str(exc) now begins with the originating tmux subcommand name followed by ": ". An error from Session.last_window() used to render as "can't find window" and now renders as "last-window: can't find window".
exc.args[0] still carries the original tmux error text, and the new LibTmuxException.subcommand attribute exposes the tmux subcommand name as a separate field. raise_if_stderr() is the shared helper that populates both.
The error-payload type also changed: exc.args[0] is now str, joined with "\n" when tmux emitted multiple stderr lines. Previously it was list[str]. Code that pattern-matches on str(exc) exactly, anchors a regex with ^ against the old shape, or indexes exc.args[0] element-by-element will no longer match. Substring matches ("can't find" in str(exc)) and unanchored re.search patterns continue to work unchanged.
See the migration guide for the upgrade pattern.
Server.sessions, Server.clients, Server.search_sessions raise on tmux errorsPreviously, a tmux command failure under Server.sessions, Server.clients, or Server.search_sessions() could return an empty QueryList indistinguishable from "no sessions / no clients attached" or "filter matched nothing". They now let LibTmuxException propagate for real tmux failures.
Genuine empty results — a server with no attached clients, or a filter that matched zero sessions — still return an empty QueryList. A missing or not-yet-started tmux server is also still treated as an empty result, preserving the historic contract that a fresh Server can be safely introspected before its daemon is up. Other tmux errors, such as socket permission failures or unsupported flags, now surface.
Client object and Server.clients accessor. New typed dataclass for tmux's attached-client model. client_name is the stable identifier; attached_session / attached_window / attached_pane re-read list-clients before resolving. attached_pane follows the attached session's current window — that can differ from the per-client active pane set by select-pane -P.Server.display_message and Window.display_message. Server reads like #{version} and #{socket_path} work without a pane handle; window reads (#{window_zoomed_flag}, #{window_active_clients_list}) auto-bind to the window's id. All three display_message wrappers (including the existing Pane.display_message) surface tmux stderr via warnings.warn rather than dropping it silently.search_*(). Server.search_panes(), search_windows(), search_sessions() plus Session/Window analogues take a filter= kwarg routed to tmux's -f flag. tmux evaluates the expression and returns only matching objects. Caveat: tmux silently expands a malformed expression to empty, which it treats as false — a typo looks identical to "no matches".Pane.send_keys(cmd=None, …) flag-only invocation. Pass cmd=None together with reset=True or repeat=N to invoke tmux's flag-only send-keys -R / send-keys -N <n> form without any trailing key argument.Server.list_buffers(format_string=, filter=). Project a chosen -F template or push a buffer-name match expression through tmux's format engine — same bad-filter caveat as the search_* methods.Server.run_shell(cwd=, show_stderr=). New kwargs map to tmux's -c (3.4+) and -E (3.6+) flags. Older tmux warns and ignores the kwarg instead of erroring.Pane.capture_pane(pending=True). Return bytes tmux has read from the pane but not yet committed to the terminal — useful for diagnosing programs whose output stalls mid-sequence.Pane / Window / Session / Client expose more typed attributes for pane state (pane_dead, pane_in_mode, pane_marked, pane_synchronized, pane_path, pane_pipe), window state (window_zoomed_flag, window_silence_flag, window_flags), session state (session_marked), and client state (client_session, client_readonly, client_termtype). Tokens the running tmux does not support stay None instead of making the listing fail.Pane.reset() now clears pane scrollback. In 0.56.0 the history clear silently no-op'd, leaving the scrollback intact (#650).session.pane_id reflects the active pane in the session's current window.Full Changelog: v0.56.0...v0.57.0
libtmux 0.57.0 broadens tmux support around attached clients, tmux-native filtering, and format-token fields. {class}`~libtmux.Client` gives callers a typed object for attached terminals, search_*() methods let tmux return only matching sessions, windows, and panes, and more tmux format tokens are exposed as typed attributes. {exc}`~libtmux.exc.LibTmuxException` now records which tmux subcommand failed, making command errors easier to handle downstream.
### Breaking changes
#### LibTmuxException string form gains a subcommand prefix (#672)
When {exc}`~libtmux.exc.LibTmuxException` is raised from a libtmux method, str(exc) now begins with the originating tmux subcommand name followed by ": ". For example, an error from {meth}`~libtmux.Session.last_window` used to render as "can't find window" and now renders as "last-window: can't find window".
exc.args[0] still carries the original tmux error text, and the new {attr}`~libtmux.exc.LibTmuxException.subcommand` attribute exposes the tmux subcommand name as a separate field. {func}`~libtmux.common.raise_if_stderr` is the shared helper that populates both.
The error-payload type changed: exc.args[0] is now str, joined with "\n" when tmux emitted multiple stderr lines. Previously it was list[str].
This is a serialization-format change: code that pattern-matches on str(exc) exactly, anchors a regex with ^ against the old shape, or indexes exc.args[0] element-by-element will no longer match.
```python # Before try:
session.last_window()
...
# After — dispatch on the typed attribute try:
session.last_window()
...
# Or — match against the original tmux error text without the prefix try:
session.last_window()
...
```
Substring matches ("can't find" in str(exc)) and unanchored re.search patterns continue to work unchanged.
#### Server.sessions, Server.clients, and Server.search_sessions raise on tmux errors (#672)
Previously, a tmux command failure under {attr}`~libtmux.Server.sessions`, {attr}`~libtmux.Server.clients`, or {meth}`~libtmux.Server.search_sessions` could return an empty {class}`~libtmux._internal.query_list.QueryList` indistinguishable from "no sessions / no clients attached" or "filter matched nothing". Those accessors now let {exc}`~libtmux.exc.LibTmuxException` propagate for real tmux failures.
Genuine empty results — a server with no attached clients, or a filter that matched zero sessions — still return an empty QueryList. A missing or not-yet-started tmux server is also still treated as an empty result, preserving the historic contract that a fresh {class}`~libtmux.Server` can be safely introspected before its daemon is up. Other tmux errors, such as socket permission failures or unsupported flags, now surface.
```python # Before — silent on tmux failure if not server.clients:
log("no clients") # also runs when tmux itself crashed
# After — distinguish the two cases try:
clients = server.clients
log(f"list-clients failed: {exc}")
log("no clients")
```
### What's new
#### Client object and Server.clients accessor (#672)
New {class}`~libtmux.Client` and {attr}`~libtmux.Server.clients` support tmux's attached-client model directly. Reads like client.client_readonly and client.client_session work on the client object without dropping down to {meth}`~libtmux.Server.cmd`.
Note that client.session_id / client.window_id / client.pane_id reflect the client's attached view when the object was read — {meth}`~libtmux.Client.refresh` re-reads them after the client switches focus. client.client_name is the client's stable identifier.
For typed access to the live attachment, use {attr}`~libtmux.Client.attached_session`, {attr}`~libtmux.Client.attached_window`, and {attr}`~libtmux.Client.attached_pane`. Each property re-reads the client from list-clients before resolving; if tmux no longer reports that client_name, such as after the client detaches, the property returns None. Otherwise, the returned {class}`~libtmux.Session` / {class}`~libtmux.Window` / {class}`~libtmux.Pane` reflects where the client is attached now, not where it was when the {class}`~libtmux.Client` was constructed. Direct {meth}`~libtmux.Client.refresh` and {meth}`~libtmux.Client.from_client_name` calls still surface missing client lookup errors.
{attr}`~libtmux.Client.attached_pane` follows the attached session's current window. That can differ from the per-client active pane set by select-pane -P; see {attr}`~libtmux.Client.attached_pane` for the exact behavior.
#### Server.display_message and Window.display_message (#672)
{meth}`~libtmux.Server.display_message` and {meth}`~libtmux.Window.display_message` join the existing {meth}`~libtmux.Pane.display_message`. Server reads like #{version} and #{socket_path} work without a pane handle; window reads (#{window_zoomed_flag}, #{window_active_clients_list}) auto-bind to the window's id.
All three wrappers surface tmux stderr via warnings.warn rather than dropping it silently. tmux uses stderr for both genuine errors and informational messages on some versions, so the wrappers warn rather than raise; callers that want to escalate can wrap the call in warnings.catch_warnings with filterwarnings("error"). See {doc}`migration` for the escalation pattern.
#### tmux-native filtering with search_*() (#672)
{meth}`~libtmux.Server.search_panes`, {meth}`~libtmux.Server.search_windows`, {meth}`~libtmux.Server.search_sessions`, and the Session/Window analogues take a filter= kwarg routed to tmux's -f flag. tmux evaluates the expression and returns only matching objects.
Caveat: tmux silently expands a malformed filter expression to empty, which it treats as false — a typo looks identical to "no matches". Verify filter syntax against the FORMATS section of tmux(1).
#### Pane.send_keys(cmd=None, …) flag-only invocation (#672)
{meth}`~libtmux.Pane.send_keys` accepts cmd=None together with reset=True or repeat=N to invoke tmux's flag-only send-keys -R / send-keys -N <n> form without any trailing key argument.
#### Server.list_buffers(format_string=, filter=) (#672)
{meth}`~libtmux.Server.list_buffers` gains format_string (-F) and filter (-f) kwargs. Callers can ask tmux to return selected fields (e.g. "#{buffer_name}") or only buffers matching an expression — same bad-filter caveat as the search_* methods.
#### Server.run_shell(cwd=, show_stderr=) (#672)
{meth}`~libtmux.Server.run_shell` gains cwd (-c) to set the shell command's working directory and show_stderr (-E) to merge the command's stderr into the captured output. Both kwargs are version-gated; older tmux warns and ignores the flag instead of erroring.
#### Pane.capture_pane(pending=True) (#672)
{meth}`~libtmux.Pane.capture_pane` gains a pending kwarg that returns bytes tmux has read from the pane but not yet committed to the terminal — useful for diagnosing programs whose output stalls mid-sequence.
#### More format-token fields on tmux objects (#672)
libtmux now asks each list-* subcommand for the format tokens that make sense for that object and tmux version. Tokens the running tmux does not support stay None instead of making the listing fail.
{class}`~libtmux.Pane`, {class}`~libtmux.Window`, {class}`~libtmux.Session`, and {class}`~libtmux.Client` expose more typed attributes for pane state (pane_dead, pane_in_mode, pane_marked, pane_synchronized, pane_path, pane_pipe), window state (window_zoomed_flag, window_silence_flag, window_flags), session state (session_marked), and client state (client_session, client_readonly, client_termtype). Some fields describe the active child object tmux reports with the row: for example, session.pane_id is the active pane in the session's current window, not a separate "session pane." See {ref}`format-tokens` for details.
### Fixes
{meth}`~libtmux.Pane.reset` now clears pane scrollback. In 0.56.0 the history clear silently no-op'd, leaving the scrollback intact (#650).
### Documentation
New API page: {doc}`api/libtmux.client`.
New {ref}`format-tokens` topic explains why some fields describe an active child object, such as session.pane_id reflecting the active pane in the session's current window (#672).
No breaking changes — public API is purely additive.
The tmux command parity release. ~50 new public methods land across Server, Pane, Window, and Session, plus expanded flag coverage on existing wrappers, plus a control_mode test fixture that lets commands requiring an attached client run under CI without a TTY. Net result: callers no longer need to drop down to Server.cmd(...) for the commands tmux ships out of the box.
No breaking changes — public API is purely additive.
New wrappers for commands that require an attached client:
Pane.display_popup, Pane.display_panesPane.choose_buffer, Pane.choose_client, Pane.choose_treeServer.display_menu, Server.command_prompt, Server.confirm_beforeSession.detach_client, Server.detach_client, Server.detach_all_clientsThe three detach-client wrappers each map to one tmux flag group exactly, with a single subprocess call:
| Wrapper | tmux invocation | Scope |
|---|---|---|
Session.detach_client() |
tmux detach-client -s <session_id> |
every client in this session |
Server.detach_client(target_client=...) |
tmux detach-client [-t <client>] |
server-wide single-client lookup |
Server.detach_all_clients(target_client=...) |
tmux detach-client -a [-t <keep>] |
server-wide, optionally preserving one client |
Round-trip pane content through named tmux buffers — useful for clipboard interop and inter-process data hand-off:
Server.set_buffer, Server.show_buffer, Server.delete_bufferServer.save_buffer, Server.load_buffer, Server.list_buffersPane.paste_bufferbind_key, unbind_key, list_keys, list_commands, run_shell, if_shell, source_file, list_clients, start_server, lock_server, lock_client, refresh_client, suspend_client, server_access, show_messages, show_prompt_history, clear_prompt_historylock_sessionsend_prefixswap, link, unlink, respawn, last_pane, next_layout, previous_layout, rotateswap, join, break_pane, move, pipe, clear_history, respawn, copy_mode, clock_mode, customize_mode, find_windowwait_forlast_window, next_window, previous_windowMost pre-existing wrappers now expose the remaining tmux flags:
Pane.send_keys — literal, hex_keys, key_name, expand_formats, target_client, repeat, reset, copy_mode_cmdPane.split — percentage=Pane.capture_pane — alternate_screen, quiet, mode_screen, to_buffer (writes capture into a tmux buffer instead of returning it)Pane.display_message — format_string, verbose, delay, notify, update_pane, target_clientPane.display_popup — target_client=, -cWindow.move_window — after, before, kill, renumberWindow.select_layout — spread, next, previousServer.new_session — detach_others, no_size, configSession.new_window — kill_existing, select_existingEnvironmentMixin.set_environment — expand_format, hiddenOptionsMixin.show_options — quietcontrol_mode pytest fixtureA new fixture spawns a real tmux -C attach-session subprocess that registers as a real client, satisfying commands like Pane.display_popup, Session.detach_client, Server.command_prompt, and Server.confirm_before — no TTY required. Exposed via libtmux.pytest_plugin and into the doctest namespace via conftest.py.
def test_display_popup(control_mode) -> None:
with control_mode() as ctl:
# commands needing an attached client now work
assert ctl.client_name != ""Window.move_window() returns up-to-date state after the move. Previously left the Window object pointing at its pre-move index, so attribute reads could appear stale until a manual refresh() call. The moved window now refreshes automatically.Pane.swap makes target optional when move_up/move_down is set, since tmux's swap-pane -U/-D already imply the target.OptionsMixin.show_options wires quiet= through the public API, fixing a flag that was previously inert.Window.last_pane calls tmux last-pane directly instead of select-pane -l, exposing its native flags (disable_input=-d, enable_input=-e, keep_zoom=-Z) with correct mappings per cmd-select-pane.c.TMUX_MAX_VERSION bumped 3.6 → 3.7, unlocking method coverage already version-gated for tmux 3.7 — notably Server.command_prompt(bspace_exit=...) and Server.show_messages(terminals=..., jobs=...). No effect on installations running tmux 3.6 or earlier.has_gte_version(...) with a warnings.warn(stacklevel=2) fallback when the running tmux is too old, matching the existing trim_trailing pattern... versionadded:: 0.56 so downstream readers can see availability at a glance.gp-sphinx docs stack to v0.0.1a16 — docs site now renders via gp-furo-theme, a Tailwind v4 respin of Furo, with sphinx-vite-builder handling theme-asset builds (#666).test_no_server_* and test_raise_if_dead_no_server_raises now reuse the server fixture for a unique socket name and finalizer cleanup, instead of hardcoded socket names with no finalizer that broke whenever a stale tmux daemon survived at the same path (#665, fixes #664).server fixture for "no server" tests by @tony in #665Full Changelog: v0.55.1...v0.56.0
libtmux 0.56.0 is the tmux command-parity release. It adds more than
50 commands across {class}~libtmux.Server, {class}~libtmux.Session,
{class}~libtmux.Window, and {class}~libtmux.Pane, filling in many
commands that previously required raw {meth}~libtmux.Server.cmd calls.
It also adds attached-client test support so interactive tmux commands can be
covered in headless test suites.
libtmux now exposes Python commands for tmux that normally depend on
an attached client: {meth}~libtmux.Pane.display_popup,
{meth}~libtmux.Server.display_menu,
{meth}~libtmux.Server.command_prompt,
{meth}~libtmux.Server.confirm_before,
{meth}~libtmux.Session.detach_client,
{meth}~libtmux.Server.detach_client,
{meth}~libtmux.Server.detach_all_clients,
{meth}~libtmux.Pane.display_panes,
{meth}~libtmux.Pane.choose_buffer,
{meth}~libtmux.Pane.choose_client, and
{meth}~libtmux.Pane.choose_tree.
The detach-client API is split by the same scopes tmux actually honors:
{meth}~libtmux.Session.detach_client maps to
tmux detach-client -s <session_id>,
{meth}~libtmux.Server.detach_client maps to
tmux detach-client [-t <client>], and
{meth}~libtmux.Server.detach_all_clients maps to
tmux detach-client -a [-t <keep>]. This keeps each method to one tmux flag
group and one subprocess call.
Named tmux buffers can now be used from libtmux without hand-built commands.
{meth}~libtmux.Server.set_buffer,
{meth}~libtmux.Server.show_buffer,
{meth}~libtmux.Server.delete_buffer,
{meth}~libtmux.Server.save_buffer,
{meth}~libtmux.Server.load_buffer,
{meth}~libtmux.Server.list_buffers, and
{meth}~libtmux.Pane.paste_buffer cover the common clipboard, capture, and
inter-process handoff workflows.
{class}~libtmux.Server gains support for key-binding inspection and mutation
({meth}~libtmux.Server.bind_key,
{meth}~libtmux.Server.unbind_key,
{meth}~libtmux.Server.list_keys,
{meth}~libtmux.Server.list_commands), shell/config execution
({meth}~libtmux.Server.run_shell,
{meth}~libtmux.Server.if_shell,
{meth}~libtmux.Server.source_file), and client/server control
({meth}~libtmux.Server.list_clients,
{meth}~libtmux.Server.start_server,
{meth}~libtmux.Server.lock_server,
{meth}~libtmux.Server.lock_client,
{meth}~libtmux.Server.refresh_client,
{meth}~libtmux.Server.suspend_client,
{meth}~libtmux.Server.server_access,
{meth}~libtmux.Server.show_messages,
{meth}~libtmux.Server.show_prompt_history,
{meth}~libtmux.Server.clear_prompt_history). {class}~libtmux.Session
also gains {meth}~libtmux.Session.lock_session, and
{class}~libtmux.Pane gains {meth}~libtmux.Pane.send_prefix.
Window-level operations now include {meth}~libtmux.Window.swap,
{meth}~libtmux.Window.link, {meth}~libtmux.Window.unlink,
{meth}~libtmux.Window.respawn, {meth}~libtmux.Window.last_pane,
{meth}~libtmux.Window.next_layout,
{meth}~libtmux.Window.previous_layout, and
{meth}~libtmux.Window.rotate. Pane-level operations now include
{meth}~libtmux.Pane.swap, {meth}~libtmux.Pane.join,
{meth}~libtmux.Pane.break_pane, {meth}~libtmux.Pane.move,
{meth}~libtmux.Pane.pipe, {meth}~libtmux.Pane.clear_history,
{meth}~libtmux.Pane.respawn, {meth}~libtmux.Pane.copy_mode,
{meth}~libtmux.Pane.clock_mode,
{meth}~libtmux.Pane.customize_mode, and
{meth}~libtmux.Pane.find_window.
Navigation helpers fill in the surrounding topology:
{meth}~libtmux.Server.wait_for,
{meth}~libtmux.Session.last_window,
{meth}~libtmux.Session.next_window, and
{meth}~libtmux.Session.previous_window.
Several established methods now surface tmux flags that were previously only
available by dropping to raw commands. Highlights include
{meth}~libtmux.Pane.send_keys (literal, hex_keys, key_name,
expand_formats, target_client), {meth}~libtmux.Pane.split
(percentage), {meth}~libtmux.Pane.capture_pane
(alternate_screen, quiet, buffer output), and
{meth}~libtmux.Pane.display_message (formatting, delay, notify, verbose, and
style/update controls).
Window/session/server additions include extra flags on
{meth}~libtmux.Window.move_window,
{meth}~libtmux.Window.select_layout,
{meth}~libtmux.Server.new_session,
{meth}~libtmux.Session.new_window,
{meth}~libtmux.Server.set_environment, and
{meth}~libtmux.Server.show_options.
control_mode (#653)The {fixture}control_mode fixture starts a real tmux -C client attached to
the test session. Downstream test suites can exercise commands such as
{meth}~libtmux.Pane.display_popup,
{meth}~libtmux.Session.detach_client,
{meth}~libtmux.Server.command_prompt, and
{meth}~libtmux.Server.confirm_before without requiring an interactive TTY.
The fixture is exported by {mod}libtmux.pytest_plugin.
Window.move_window() refreshes moved windows (#653){meth}~libtmux.Window.move_window now refreshes the moved
{class}~libtmux.Window after tmux completes the move. Code that reads
attributes after a relative or cross-session move no longer needs a manual
{meth}~libtmux.Window.refresh call to avoid stale state.
The documentation stack now uses gp-furo-theme, a Tailwind v4 respin of Furo,
with sphinx-vite-builder owning theme asset builds. This picks up the shared
gp-sphinx documentation surface used by sibling projects.
test_no_server_* and test_raise_if_dead_no_server_raises now use the
{fixture}server fixture for unique socket names and finalizer cleanup. The
tests no longer fail just because a stale tmux daemon survived at one of the
old hardcoded socket paths. Fixes #664.
{data}~libtmux.common.TMUX_MAX_VERSION is now "3.7", enabling support and
tests for version-gated tmux 3.7 flags such as
{meth}~libtmux.Server.command_prompt bspace_exit and
{meth}~libtmux.Server.show_messages terminals / jobs. Installations on
tmux 3.6 and earlier keep their existing behavior.
libtmux 0.56.0 is the tmux command-parity release. It adds more than 50 commands across Server , Session , Window , and Pane , filling in many commands that previously required raw cmd() calls. It also adds attached-client test support so interactive tmux commands can be covered in headless test suites.
libtmux now exposes Python commands for tmux that normally depend on an attached client: display_popup() , display_menu() , command_prompt() , confirm_before() , detach_client() , detach_client() , detach_all_clients() , display_panes() , choose_buffer() , choose_client() , and choose_tree() .
The detach-client API is split by the same scopes tmux actually honors: detach_client() maps to tmux detach-client -s <session_id> , detach_client() maps to tmux detach-client [-t <client>] , and detach_all_clients() maps to tmux detach-client -a [-t <keep>] . This keeps each method to one tmux flag group and one subprocess call.
Named tmux buffers can now be used from libtmux without hand-built commands. set_buffer() , show_buffer() , delete_buffer() , save_buffer() , load_buffer() , list_buffers() , and paste_buffer() cover the common clipboard, capture, and inter-process handoff workflows.
Server gains support for key-binding inspection and mutation ( bind_key() , unbind_key() , list_keys() , list_commands() ), shell/config execution ( run_shell() , if_shell() , source_file() ), and client/server control ( list_clients() , start_server() , lock_server() , lock_client() , refresh_client() , suspend_client() , server_access() , show_messages() , show_prompt_history() , clear_prompt_history() ). Session also gains lock_session() , and Pane gains send_prefix() .
Window-level operations now include swap() , link() , unlink() , respawn() , last_pane() , next_layout() , previous_layout() , and rotate() . Pane-level operations now include swap() , join() , break_pane() , move() , pipe() , clear_history() , respawn() , copy_mode() , clock_mode() , customize_mode() , and find_window() .
Navigation helpers fill in the surrounding topology: wait_for() , last_window() , next_window() , and previous_window() .
Several established methods now surface tmux flags that were previously only available by dropping to raw commands. Highlights include send_keys() ( literal , hex_keys , key_name , expand_formats , target_client ), split() ( percentage ), capture_pane() ( alternate_screen , quiet , buffer output), and display_message() (formatting, delay, notify, verbose, and style/update controls).
Window/session/server additions include extra flags on move_window() , select_layout() , new_session() , new_window() , set_environment() , and show_options() .
The control_mode fixture starts a real tmux -C client attached to the test session. Downstream test suites can exercise commands such as display_popup() , detach_client() , command_prompt() , and confirm_before() without requiring an interactive TTY. The fixture is exported by libtmux.pytest_plugin .
move_window() now refreshes the moved Window after tmux completes the move. Code that reads attributes after a relative or cross-session move no longer needs a manual refresh() call to avoid stale state.
The documentation stack now uses gp-furo-theme , a Tailwind v4 respin of Furo, with sphinx-vite-builder owning theme asset builds. This picks up the shared gp-sphinx documentation surface used by sibling projects.
test_no_server_* and test_raise_if_dead_no_server_raises now use the server fixture for unique socket names and finalizer cleanup. The tests no longer fail just because a stale tmux daemon survived at one of the old hardcoded socket paths. Fixes #664 .
TMUX_MAX_VERSION is now "3.7" , enabling support and tests for version-gated tmux 3.7 flags such as command_prompt() bspace_exit and show_messages() terminals / jobs . Installations on tmux 3.6 and earlier keep their existing behavior.
A point release focused on a pytest-plugin cleanup fix, a new Sphinx extension for documenting pytest fixtures, and a docs-stack migration to gp-sphin
A point release focused on a pytest-plugin cleanup fix, a new Sphinx extension for documenting pytest fixtures, and a docs-stack migration to gp-sphinx.
pytest_plugin leaks tmux socket files on teardown (#661, fixes #660)The server and TestServer fixtures now unlink(2) the tmux socket from /tmp/tmux-<uid>/ during teardown, in addition to calling server.kill(). tmux does not reliably remove its own socket on non-graceful exit, so /tmp/tmux-<uid>/ would accumulate stale libtmux_test* entries across runs — 10k+ observed on long-lived dev machines.
A new internal _reap_test_server helper centralizes the kill + unlink flow and suppresses cleanup-time errors, so a finalizer failure can no longer mask the real test failure.
If you use libtmux's pytest plugin in CI or locally, upgrade to stop the leak.
A new Sphinx extension (docs/_ext/sphinx_pytest_fixtures.py) renders pytest fixtures as first-class API documentation with scope/kind/factory badges, cross-referenced dependencies, and auto-generated usage snippets.
.. autofixture:: — autodoc-style documenter for individual fixtures.. autofixtures:: — bulk discovery and rendering from a module.. autofixture-index:: — auto-generated index table with linked return types (via intersphinx) and parsed RST descriptions:fixture: cross-reference role with short-name resolutionAlso fixes an inaccurate session_params fixture docstring surfaced by the new extension.
v0.0.1a8 (#659); subsequent bump to v0.0.1a9 on mastertypes-docutils to dev dependencies for mypy type checking (#656)Full Changelog: v0.55.0...v0.55.1
libtmux 0.55.1 is a documentation and test-isolation release. It makes the pytest plugin's server cleanup more robust, documents fixtures as first-class API objects, and brings the docs site onto the shared gp-sphinx visual stack.
The docs now render pytest fixtures with autodoc-style directives for individual fixtures, bulk fixture listings, and a fixture index. Fixture pages show scope, kind, factory metadata, dependencies, generated usage examples, and responsive badge styling through the gp-sphinx fixture extension.
The {fixture}server and {fixture}TestServer finalizers now unlink tmux
socket files under /tmp/tmux-<uid>/ after killing the server. This prevents
long-lived development machines from accumulating stale libtmux_test*
sockets that can break later test runs. Fixes #660.
session_params fixture docs report the right return type (#656)The {fixture}session_params fixture docstring now describes the value pytest
actually injects.
API pages pick up gp-sphinx card layouts, badges, MyST-aware cross references, argparse-label fixes, IBM Plex typography, and the 0.0.1a8 docs-stack bump.
types-docutils is available for type checking (#656)The dev dependency set now includes types-docutils, keeping the fixture-docs
extension visible to mypy.
via @tony in https://github.com/tmux-python/libtmux/pull/636
via @tony in https://github.com/tmux-python/libtmux/pull/636
New Pane.set_title() method wraps select-pane -T and returns the pane
for method chaining. A Pane.title property aliases pane_title for
convenience:
pane.set_title("my-worker")
pane.pane_title # 'my-worker'
pane.title # 'my-worker'
The pane_title format variable is now included in libtmux's pane format
queries (it was previously excluded via an incorrect "removed in 3.1+" comment).
Server now accepts a tmux_bin parameter to use an alternative binary
(e.g. wemux, byobu, or a custom build):
server = Server(socket_name="myserver", tmux_bin="/usr/local/bin/tmux-next")
The path is threaded through Server.cmd(), Server.raise_if_dead(),
fetch_objs(), all version-check functions (has_version,
has_gte_version, etc.), and hook scope guards in HooksMixin. Child
objects (Session, Window, Pane) inherit it automatically. Falls back to
shutil.which("tmux") when not set.
tmux_cmd now emits a structured DEBUG log record with
extra={"tmux_cmd": ...} before invoking the subprocess, using
shlex.join for POSIX-correct quoting. This complements the existing
post-execution stdout log and is a prerequisite for a future dry-run mode.
Passing a non-existent binary path previously surfaced as a raw
FileNotFoundError from subprocess. Both tmux_cmd and
raise_if_dead now catch FileNotFoundError and raise
TmuxCommandNotFound consistently.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.54.0...v0.55.
libtmux 0.55.0 improves pane naming, custom tmux binary support, and command diagnostics. The release is useful for projects that run against alternate tmux builds or need clearer subprocess logging.
{meth}~libtmux.Pane.set_title wraps select-pane -T, and
{attr}~libtmux.Pane.title is a short alias for
{attr}~libtmux.Pane.pane_title.
pane.set_title("my-worker")
pane.pane_title
# 'my-worker'
pane.title
# 'my-worker'
The pane_title tmux format variable is now included in libtmux format
queries.
Server can use an explicit tmux binary (#636){class}~libtmux.Server accepts tmux_bin for alternate binaries such as
wemux, byobu, or locally built tmux checkouts.
server = Server(socket_name="myserver", tmux_bin="/usr/local/bin/tmux-next")
The binary path flows through {meth}~libtmux.Server.cmd,
{meth}~libtmux.Server.raise_if_dead, {func}~libtmux.neo.fetch_objs,
version checks such as {func}~libtmux.common.has_version and
{func}~libtmux.common.has_gte_version, and option/hook scope guards. Child
objects inherit it automatically. When tmux_bin is omitted, command execution
falls back to shutil.which("tmux").
{func}~libtmux.common.tmux_cmd logs the full command line at DEBUG before
running it, complementing the existing post-execution output logging and giving
operators a better diagnostic trail.
tmux_bin paths raise TmuxCommandNotFound (#636)Passing a missing binary path now raises
{exc}~libtmux.exc.TmuxCommandNotFound consistently in both
{func}~libtmux.common.tmux_cmd and
{meth}~libtmux.Server.raise_if_dead, instead of leaking a raw
FileNotFoundError.
libtmux 0.55.0 improves pane naming, custom tmux binary support, and command diagnostics. The release is useful for projects that run against alternate tmux builds or need clearer subprocess logging.
set_title() wraps select-pane -T , and title is a short alias for pane_title .
pane . set_title ( "my-worker" ) pane . pane_title # 'my-worker' pane . title # 'my-worker'
The pane_title tmux format variable is now included in libtmux format queries.
Server accepts tmux_bin for alternate binaries such as wemux, byobu, or locally built tmux checkouts.
server = Server ( socket_name = "myserver" , tmux_bin = "/usr/local/bin/tmux-next" )
The binary path flows through cmd() , raise_if_dead() , fetch_objs() , version checks such as has_version() and has_gte_version() , and option/hook scope guards. Child objects inherit it automatically. When tmux_bin is omitted, command execution falls back to shutil.which("tmux") .
tmux_cmd() logs the full command line at DEBUG before running it, complementing the existing post-execution output logging and giving operators a better diagnostic trail.
Passing a missing binary path now raises TmuxCommandNotFound consistently in both tmux_cmd() and raise_if_dead() , instead of leaking a raw FileNotFoundError .
Structured lifecycle logging across Server, Session, Window, and Pane with filterable extra context
extra contextrename_window(), Server.kill(), new_session(), and kill_window() error handlingAll lifecycle operations (create, kill, rename, split) now emit INFO-level log records with structured extra context. Every log call includes scalar keys for filtering in log aggregators and test assertions via caplog.records:
| Key | Type | Context |
|---|---|---|
tmux_subcommand |
str |
tmux subcommand (e.g. new-session) |
tmux_target |
str |
tmux target specifier |
tmux_session |
str |
session name |
tmux_window |
str |
window name or index |
tmux_pane |
str |
pane identifier |
Logging hygiene improvements:
__init__.py per Python logging best practicestmux_cmd execution with isEnabledFor guards and heavy keys (tmux_stdout, tmux_stderr, tmux_stdout_len, tmux_stderr_len)%s throughouttraceback.print_stack() with logger.debug(exc_info=True)logger.exception() with logger.warning() and tmux_option_key structured context for recoverable parse failuresPreviously rename_window() caught all exceptions and logged them, masking tmux errors. It now propagates the error, consistent with all other command methods.
Server.kill() previously discarded the tmux return value. It now checks stderr, raises on unexpected errors, and silently returns for expected conditions ("no server running", "error connecting to").
When kill_session=True and the existing session kill fails, new_session() now raises LibTmuxException with the stderr instead of proceeding silently.
Fixed self.window_name (wrong attribute) to self.session_name when formatting tmux targets for integer target_window values. Also widened the type signature from str | None to str | int | None to match the existing isinstance(target_window, int) branch.
pip:
$ pip install libtmux==0.54.0
uv:
$ uv add libtmux==0.54.0
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.53.1...v0.54.0
libtmux 0.54.0 adds structured lifecycle logging and tightens failure handling for important object operations. Applications can now filter logs by stable tmux context fields instead of parsing message text.
Create, kill, rename, split, and command-execution paths now log with stable
extra keys such as tmux_subcommand, tmux_session, tmux_window,
tmux_pane, and tmux_target. The library also installs a NullHandler,
uses lazy %s logging, guards heavier debug data, and removes ad-hoc stack
printing.
Window.rename_window() now propagates tmux failures (#637){meth}~libtmux.Window.rename_window no longer catches and logs every
exception. tmux errors now surface to callers like the rest of the command API.
Server.kill() handles expected dead-server states (#637){meth}~libtmux.Server.kill now captures stderr, ignores expected "no server
running" style states, and raises for unexpected tmux errors.
Server.new_session(kill_session=True) checks failed cleanup (#637)If the pre-create kill-session step fails, {meth}~libtmux.Server.new_session
now raises {exc}~libtmux.exc.LibTmuxException instead of continuing as though
the old session had been removed.
Fix race condition in new_session() by avoiding list-sessions query by @neubig in https://github.com/tmux-python/libtmux/pull/625
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.53.0...v0.53.1
libtmux 0.53.1 fixes a session-creation race and moves the developer command surface from Make to Just.
Server.new_session() no longer races its own initialization query (#625){meth}~libtmux.Server.new_session now parses all required session fields from
the tmux new-session -P output instead of creating the session and then
immediately querying list-sessions. That removes a race that could raise
{exc}~libtmux.exc.TmuxObjectDoesNotExist in PyInstaller, Python 3.13+, Docker,
and other slower-startup environments. Fixes #624. Thanks @neubig.
The docs deployment path moved to GitHub OIDC and AWS CLI credentials.
just (#617)The project moved from Makefile to justfile, and docs now reference the
just command names.
A focused maintenance release that fixes a critical bug in Session.attach() that caused tracebacks when users killed sessions while attached via tmuxp
A focused maintenance release that fixes a critical bug in Session.attach() that caused tracebacks when users killed sessions while attached via tmuxp load.
Session.attach() no longer raises TmuxObjectDoesNotExist when a user kills the session during attachmentSession.attach() no longer calls refresh() after returning (semantically incorrect for interactive commands)Fixed an issue where Session.attach() would raise TmuxObjectDoesNotExist when a user:
tmuxp loadAfter running tmuxp load, users would see this traceback printed to their terminal after detaching:
Traceback (most recent call last):
File "~/.local/bin/tmuxp", line 7, in <module>
sys.exit(cli.cli())
...
File ".../libtmux/session.py", line 332, in attach
self.refresh()
File ".../libtmux/neo.py", line 167, in _refresh
obj = fetch_obj(...)
File ".../libtmux/neo.py", line 242, in fetch_obj
raise exc.TmuxObjectDoesNotExist(...)
libtmux.exc.TmuxObjectDoesNotExist: Could not find object
Session.attach() called self.refresh() after the attach-session command returned. Since attach-session is a blocking interactive command, the session state can change arbitrarily during attachment—including being killed entirely.
The refresh() call was semantically incorrect for interactive commands:
attach-session blocks until user detachesThe fix: Remove the self.refresh() call from Session.attach() (2 lines removed).
Session.attach() previously called Session.refresh() after the attach-session command returned. This was semantically incorrect since attach-session is a blocking interactive command where session state can change arbitrarily during attachment.
This was never strictly defined behavior as libtmux abstracts tmux internals away. Code that relied on the session object being refreshed after attach() should explicitly call session.refresh() if needed.
# If you relied on the implicit refresh (unlikely):
session.attach()
session.refresh() # Now explicit if you need it
| Date | Event |
|---|---|
| Feb 2024 | Session.attach() added with refresh() call (9a5147aa) |
| Nov 2025 | tmuxp switched from attach_session() to attach() (fdafdd2b) |
| Dec 2025 | Users started experiencing the bug |
| Dec 2025 | v0.53.0 released with fix |
pip:
pip install libtmux==0.53.0
uv:
uv add libtmux==0.53.0
pipx (for tmuxp users):
pipx upgrade tmuxp
Session.attach()): Remove refresh() call that fails after session killed by @tony in https://github.com/tmux-python/libtmux/pull/616Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.52.1...v0.53.0
libtmux 0.53.0 narrows the semantics of interactive attachment and fixes a shutdown edge case around attached sessions.
Session.attach() no longer refreshes after returning (#616){meth}~libtmux.Session.attach no longer calls
{meth}~libtmux.neo.Obj.refresh after attach-session returns. The tmux command
is interactive and blocking, so arbitrary state may change while the user is
attached. Code that needs fresh state after attaching should call
{meth}~libtmux.Session.refresh explicitly.
Session.attach() (#616){meth}~libtmux.Session.attach no longer raises
{exc}~libtmux.exc.TmuxObjectDoesNotExist when the attached session is killed
before the user detaches.
libtmux 0.53.0 narrows the semantics of interactive attachment and fixes a shutdown edge case around attached sessions.
attach() no longer calls refresh() after attach-session returns. The tmux command is interactive and blocking, so arbitrary state may change while the user is attached. Code that needs fresh state after attaching should call refresh() explicitly.
attach() no longer raises TmuxObjectDoesNotExist when the attached session is killed before the user detaches.
Nothing published for this version
ci(release): Migrate to PyPI Trusted Publisher by @tony in https://github.com/tmux-python/libtmux/pull/615
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.52.0...v0.52.1
libtmux 0.52.1 hardens package publishing.
PyPI releases now use OIDC-based Trusted Publisher instead of API tokens, enabling attestations and reducing long-lived secret exposure.
The Pane.capture_pane() method now supports 5 new parameters exposing additional tmux capture-pane flags:
capture_pane() enhancementsThe Pane.capture_pane() method now supports 5 new parameters exposing additional tmux capture-pane flags:
| Parameter | tmux Flag | Description |
|---|---|---|
escape_sequences |
-e |
Include ANSI escape sequences (colors, attributes) |
escape_non_printable |
-C |
Escape non-printable chars as octal \xxx |
join_wrapped |
-J |
Join wrapped lines back together |
preserve_trailing |
-N |
Preserve trailing spaces at line ends |
trim_trailing |
-T |
Trim trailing empty positions (tmux 3.4+) |
Capturing colored output:
# Capture with ANSI escape sequences preserved
pane.send_keys('printf "\\033[31mRED\\033[0m"', enter=True)
output = pane.capture_pane(escape_sequences=True)
# Output contains: '\x1b[31mRED\x1b[0m'
Joining wrapped lines:
# Long lines that wrap are joined back together
output = pane.capture_pane(join_wrapped=True)
The trim_trailing parameter requires tmux 3.4+. If used with an older version, a warning is issued and the flag is ignored. All other parameters work with libtmux's minimum supported version (tmux 3.2a).
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.51.0...v0.52.0
libtmux 0.52.0 expands pane capture so applications can preserve more of the terminal's rendered output.
Pane.capture_pane() exposes more capture-pane flags (#614){meth}~libtmux.Pane.capture_pane now supports escape_sequences,
escape_non_printable, join_wrapped, preserve_trailing, and
trim_trailing.
| Parameter | tmux flag | Description |
|---|---|---|
escape_sequences |
-e |
Include ANSI escape sequences. |
escape_non_printable |
-C |
Escape non-printable characters as octal. |
join_wrapped |
-J |
Join wrapped lines back together. |
preserve_trailing |
-N |
Preserve trailing spaces. |
trim_trailing |
-T |
Trim trailing empty positions on tmux 3.4+. |
pane.send_keys('printf "\\033[31mRED\\033[0m"', enter=True)
output = pane.capture_pane(escape_sequences=True)
Legacy API methods (deprecated in v0.16–v0.33) now raise DeprecatedError (hard error) instead of emitting DeprecationWarning.
Legacy API methods (deprecated in v0.16–v0.33) now raise DeprecatedError (hard error) instead of emitting DeprecationWarning.
See the migration guide for full context and examples.
DeprecatedError) by @tony in https://github.com/tmux-python/libtmux/pull/611| Deprecated | Replacement | Class | Deprecated Since |
|---|---|---|---|
kill_server() |
kill() |
Server | 0.30.0 |
attach_session() |
attach() |
Session | 0.30.0 |
kill_session() |
kill() |
Session | 0.30.0 |
select_window() |
select() |
Window | 0.30.0 |
kill_window() |
kill() |
Window | 0.30.0 |
split_window() |
split() |
Window | 0.33.0 |
select_pane() |
select() |
Pane | 0.30.0 |
resize_pane() |
resize() |
Pane | 0.28.0 |
split_window() |
split() |
Pane | 0.33.0 |
| Deprecated | Replacement | Class | Deprecated Since |
|---|---|---|---|
attached_window |
active_window |
Session | 0.31.0 |
attached_pane |
active_pane |
Session | 0.31.0 |
attached_pane |
active_pane |
Window | 0.31.0 |
| Deprecated | Replacement | Class | Deprecated Since |
|---|---|---|---|
list_sessions() / _list_sessions() |
sessions property |
Server | 0.17.0 |
list_windows() / _list_windows() |
windows property |
Session | 0.17.0 |
list_panes() / _list_panes() |
panes property |
Window | 0.17.0 |
where({...}) |
.filter(**kwargs) on sessions/windows/panes |
All | 0.17.0 |
find_where({...}) |
.get(default=None, **kwargs) on sessions/windows/panes |
All | 0.17.0 |
get_by_id(id) |
.get(session_id/window_id/pane_id=..., default=None) |
All | 0.16.0 |
children property |
sessions/windows/panes |
All | 0.17.0 |
| Deprecated | Replacement | Deprecated Since |
|---|---|---|
obj['key'] |
obj.key |
0.17.0 |
obj.get('key') |
obj.key |
0.17.0 |
obj.get('key', None) |
getattr(obj, 'key', None) |
0.17.0 |
The following deprecations from v0.50.0 continue to emit DeprecationWarning only:
| Deprecated | Replacement | Class |
|---|---|---|
set_window_option() |
set_option() |
Window |
show_window_option() |
show_option() |
Window |
show_window_options() |
show_options() |
Window |
g parameter |
global_ parameter |
Options & hooks methods |
Before (deprecated, now raises DeprecatedError):
# Old method names
server.kill_server()
session.attach_session()
window.split_window()
pane.resize_pane()
# Old query API
server.list_sessions()
session.find_where({'window_name': 'main'})
# Old dict-style access
window['window_name']
After:
# New method names
server.kill()
session.attach()
window.split()
pane.resize()
# New query API
server.sessions
session.windows.get(window_name='main', default=None)
# New attribute access
window.window_name
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.50.1...v0.51.0
libtmux 0.51.0 turns the long-running 0.16-0.33 deprecation period into hard
errors for the oldest API spellings. The migration target is the modern object
attribute and {class}~libtmux.common.QueryList interface documented in
{doc}migration.
DeprecatedError (#611)APIs deprecated in earlier releases now raise
{exc}~libtmux.exc.DeprecatedError instead of emitting
{class}DeprecationWarning.
| Deprecated API | Replacement | Deprecated | Raises | Note |
|---|---|---|---|---|
kill_server() |
{meth}~libtmux.Server.kill |
0.30.0 | 0.51.0 | Server |
attach_session(), kill_session() |
{meth}~libtmux.Session.attach, {meth}~libtmux.Session.kill |
0.30.0 | 0.51.0 | Session |
select_window(), kill_window(), split_window() |
{meth}~libtmux.Window.select, {meth}~libtmux.Window.kill, {meth}~libtmux.Window.split |
0.30.0 / 0.33.0 | 0.51.0 | Window |
resize_pane(), select_pane(), split_window() |
{meth}~libtmux.Pane.resize, {meth}~libtmux.Pane.select, {meth}~libtmux.Pane.split |
0.28.0 / 0.30.0 / 0.33.0 | 0.51.0 | Pane |
attached_window, attached_pane |
{attr}~libtmux.Session.active_window, {attr}~libtmux.Session.active_pane / {attr}~libtmux.Window.active_pane |
0.31.0 | 0.51.0 | Session / Window |
list_*(), _list_*(), _update_*(), children, where(), find_where(), get_by_id() |
{attr}~libtmux.Server.sessions / {attr}~libtmux.Session.windows / {attr}~libtmux.Window.panes with {meth}~libtmux.common.QueryList.filter / {meth}~libtmux.common.QueryList.get |
0.16.0 / 0.17.0 | 0.51.0 | Query helpers |
Dict-style access (obj["key"], obj.get(...)) |
Attribute access, such as {attr}~libtmux.Window.window_name |
0.17.0 | 0.51.0 | All tmux objects |
The 0.50.0 option/hook deprecations remain soft warnings for now:
| Deprecated API | Replacement | Deprecated | Note |
|---|---|---|---|
set_window_option(), show_window_option(), show_window_options() |
{meth}~libtmux.Window.set_option, {meth}~libtmux.Window.show_option, {meth}~libtmux.Window.show_options |
0.50.0 | Window |
g option parameter |
global_ on {meth}~libtmux.options.OptionsMixin.set_option, {meth}~libtmux.options.OptionsMixin.show_option, {meth}~libtmux.options.OptionsMixin.show_options |
0.50.0 | Options and hooks |
libtmux 0.51.0 turns the long-running 0.16-0.33 deprecation period into hard errors for the oldest API spellings. The migration target is the modern object attribute and QueryList interface documented in Migration notes .
APIs deprecated in earlier releases now raise DeprecatedError instead of emitting DeprecationWarning .
Deprecated API
Replacement
Deprecated
Raises
Note
kill_server()
kill()
0.30.0
0.51.0
Server
attach_session() , kill_session()
attach() , kill()
0.30.0
0.51.0
Session
select_window() , kill_window() , split_window()
select() , kill() , split()
0.30.0 / 0.33.0
0.51.0
Window
resize_pane() , select_pane() , split_window()
resize() , select() , split()
0.28.0 / 0.30.0 / 0.33.0
0.51.0
Pane
attached_window , attached_pane
active_window , active_pane / active_pane
0.31.0
0.51.0
Session / Window
list_() , list() , update*() , children , where() , find_where() , get_by_id()
sessions / windows / panes with filter() / get()
0.16.0 / 0.17.0
0.51.0
Query helpers
Dict-style access ( obj["key"] , obj.get(...) )
Attribute access, such as window_name
0.17.0
0.51.0
All tmux objects
The 0.50.0 option/hook deprecations remain soft warnings for now:
Deprecated API
Replacement
Deprecated
Note
set_window_option() , show_window_option() , show_window_options()
set_option() , show_option() , show_options()
0.50.0
Window
g option parameter
global_ on set_option() , show_option() , show_options()
0.50.0
Options and hooks
Doc fixes by @tony in https://github.com/tmux-python/libtmux/pull/612
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.50.0...v0.50.1
libtmux 0.50.1 is a documentation repair release for the 0.50 option and hook API.
The docs now use normalized headings and Sphinx module directives for more
stable anchors and indexes. Type-hints configuration was adjusted to avoid RST
indentation problems and forward-reference warnings, and
{meth}~libtmux.Pane.capture_pane / {meth}~libtmux.Pane.display_message
now document their list-returning behavior correctly.
The following methods are deprecated and will be removed in a future release:
libtmux 0.50 brings a major enhancement to option and hook management with a unified, typed API for managing tmux options and hooks across all object types.
show_option(), show_options(), set_option(), and unset_option() methods available on Server, Session, Window, and Panecommand-alias[0], command-alias[99])All tmux objects now share a consistent options interface:
import libtmux
server = libtmux.Server()
session = server.sessions[0]
window = session.windows[0]
# Get all options as a structured dict
session.show_options()
# {'activity-action': 'other', 'base-index': 0, ...}
# Get a single option value
session.show_option('base-index')
# 0
# Set an option
window.set_option('automatic-rename', True)
# Unset an option (revert to default)
window.unset_option('automatic-rename')
Programmatic control over tmux hooks:
session = server.sessions[0]
# Set a hook
session.set_hook('session-renamed', 'display-message "Renamed!"')
# Get hook value (returns SparseArray for indexed hooks)
session.show_hook('session-renamed')
# {0: 'display-message "Renamed!"'}
# Get all hooks
session.show_hooks()
# Remove a hook
session.unset_hook('session-renamed')
# Bulk operations for indexed hooks
session.set_hooks('session-renamed', {
0: 'display-message "Hook 0"',
1: 'display-message "Hook 1"',
5: 'run-shell "echo hook 5"',
})
The following methods are deprecated and will be removed in a future release:
| Deprecated | Replacement |
|---|---|
Window.set_window_option() |
Window.set_option() |
Window.show_window_option() |
Window.show_option() |
Window.show_window_options() |
Window.show_options() |
g parameterThe g parameter for global options is deprecated in favor of global_:
# Before (deprecated)
session.show_option('status', g=True)
# After (0.50.0+)
session.show_option('status', global_=True)
OptionScope enum: Server, Session, Window, PaneOPTION_SCOPE_FLAG_MAP: Maps scope to tmux flags (-s, -w, -p)HOOK_SCOPE_FLAG_MAP: Maps scope to hook flags| Feature | Minimum tmux |
|---|---|
| All options/hooks features | 3.2+ |
Window/Pane hook scopes (-w, -p) |
3.2+ |
client-active, window-resized hooks |
3.3+ |
pane-title-changed hook |
3.5+ |
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.49.0...v0.50.0
libtmux 0.50.0 is the options and hooks release. It adds one typed API for reading and writing tmux options across every object type, adds programmatic hook management, and introduces an indexed container for tmux's sparse option arrays.
The window-specific option helpers remain available but now emit
{class}DeprecationWarning. Use the shared option API instead.
| Deprecated | Replacement |
|---|---|
Window.set_window_option() |
{meth}~libtmux.Window.set_option |
Window.show_window_option() |
{meth}~libtmux.Window.show_option |
Window.show_window_options() |
{meth}~libtmux.Window.show_options |
window.set_window_option("automatic-rename", "on")
# DeprecationWarning: Window.set_window_option() is deprecated
window.set_option("automatic-rename", True)
{class}~libtmux.options.OptionsMixin gives
{class}~libtmux.Server, {class}~libtmux.Session,
{class}~libtmux.Window, and {class}~libtmux.Pane the same
{meth}~libtmux.options.OptionsMixin.show_options,
{meth}~libtmux.options.OptionsMixin.show_option,
{meth}~libtmux.options.OptionsMixin.set_option, and
{meth}~libtmux.options.OptionsMixin.unset_option methods.
session.show_option("base-index")
window.set_option("automatic-rename", True)
window.unset_option("automatic-rename")
{meth}~libtmux.options.OptionsMixin.set_option supports tmux flags for format
expansion, unsetting, global scope, child-pane cleanup, overwrite prevention,
warning suppression, and append mode.
{class}~libtmux.hooks.HooksMixin adds hook operations across the tmux object
hierarchy: set, show, list, unset, run, and bulk set indexed hook values.
session.set_hook("session-renamed", 'display-message "Renamed!"')
session.show_hook("session-renamed")
session.unset_hook("session-renamed")
{class}~libtmux._internal.sparse_array.SparseArray preserves gaps in tmux
arrays such as command-alias[0], command-alias[99], and
terminal-features[0].
The options and hooks work relies on tmux 3.2+ behavior. Hook scope flags for
windows and panes require tmux 3.2+, client-active and window-resized hooks
require tmux 3.3+, and pane-title-changed requires tmux 3.5+.
Nothing published for this version
Drop support for tmux versions < 3.2 by @tony in https://github.com/tmux-python/libtmux/pull/608
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.48.0...v0.49.0
libtmux 0.49.0 completes the tmux 3.2 baseline transition announced in 0.48.0.
{data}~libtmux.common.TMUX_MIN_VERSION is now tmux 3.2a. The
TMUX_SOFT_MIN_VERSION compatibility constant, its warning path, and older
version guards were removed. Users who need older tmux releases should stay on
libtmux 0.48.x.
Nothing published for this version
Deprecate old tmux versions by @tony in https://github.com/tmux-python/libtmux/pull/606
Deprecate old tmux versions by @tony in https://github.com/tmux-python/libtmux/pull/606
tmux: Add tmux 3.6 to testgrid by @tony in https://github.com/tmux-python/libtmux/pull/607
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.47.0...v0.48.0
libtmux 0.48.0 starts the tmux 3.2 baseline transition and adds tmux 3.6 to the compatibility grid.
tmux versions below 3.2a emit {class}FutureWarning on first use. Set
LIBTMUX_SUPPRESS_VERSION_WARNING=1 to silence the transition warning. 0.48.x
is the final line intended to support tmux below 3.2a.
The test grid now includes tmux 3.6, and {data}~libtmux.common.TMUX_MAX_VERSION
was raised from 3.4 to 3.6.
TMUX_SOFT_MIN_VERSION marks the transition floor (#606)The temporary soft-minimum constant records tmux 3.2a as the deprecation threshold for the 0.48.x compatibility window.
Support Python 3.14 by @tony in https://github.com/tmux-python/libtmux/pull/601
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.46.2...v0.47.0
libtmux 0.47.0 moves the supported Python baseline forward.
Python 3.10 is now the minimum supported Python version. Python 3.9 reached end of life on October 31, 2025; see the Python release status and PEP 596 for the upstream timeline.
The CI matrix now includes Python 3.14.
Fix new_window argument typing in Session by @Data5tream in https://github.com/tmux-python/libtmux/pull/596
new_window argument typing in Session by @Data5tream in https://github.com/tmux-python/libtmux/pull/596
StrPath typing, fix new_session by @tony in https://github.com/tmux-python/libtmux/pull/597
StrPath typing, fix new_session, part 2 by @tony in https://github.com/tmux-python/libtmux/pull/598Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.46.1...v0.46.2
libtmux 0.46.2 broadens path handling for start directories.
{meth}~libtmux.Server.new_session,
{meth}~libtmux.Session.new_window, {meth}~libtmux.Pane.split,
{meth}~libtmux.Window.split, and their compatibility aliases accept
os.PathLike values in addition to strings. Thanks @Data5tream for the
initial work.
libtmux 0.46.2 broadens path handling for start directories.
new_session() , new_window() , split() , split() , and their compatibility aliases accept os.PathLike values in addition to strings. Thanks @Data5tream for the initial work.
v0.46.x will extend the life of v0.46.0 while new features are being developed for watching for changes within libtmux panes, windows, and sessions.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.46.0...v0.46.1
v0.46.x will extend the life of v0.46.0 while new features are being developed for watching for changes within libtmux panes, windows, and sessions.
See:
Fix typo in Pane.send_keys (https://github.com/tmux-python/libtmux/pull/593)
Thank you @subbyte!
libtmux 0.46.1 is a maintenance release for the 0.46.x line.
Pane.send_keys typo fixed (#593)The {meth}~libtmux.Pane.send_keys documentation had a typo fixed. Thanks
@subbyte.
Test Helper Imports Refactored: Direct imports from libtmux.test are no longer possible. You must now import from specific submodules (#580) ```python
Test Helper Imports Refactored: Direct imports from libtmux.test are no longer possible. You must now import from specific submodules (#580)
# Before:
from libtmux.test import namer
# After:
from libtmux.test.named import namer
# Before:
from libtmux.test import RETRY_INTERVAL_SECONDS
# After:
from libtmux.test.constants import RETRY_INTERVAL_SECONDS
EnvironmentVarGuard now handles variable cleanup more reliablyThese changes improve maintainability of test helpers both internally and for downstream packages that depend on libtmux.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.45.0...v0.46.0
libtmux 0.46.0 finishes the libtmux.test helper split by removing root-level
imports.
libtmux.test were removed (#580)Import test helpers from their focused modules.
# Before 0.46.0
from libtmux.test import namer
# From 0.46.0 onward
from libtmux.test.random import namer
# Before 0.46.0
from libtmux.test import RETRY_INTERVAL_SECONDS
# From 0.46.0 onward
from libtmux.test.constants import RETRY_INTERVAL_SECONDS
The test-helper modules gained stronger coverage for environment cleanup, constants, random-name helpers, docstrings, and coverage markers.
by @tony in https://github.com/tmux-python/libtmux/pull/578
by @tony in https://github.com/tmux-python/libtmux/pull/578
Test helper functionality has been split into focused modules (#578):
libtmux.test module split into:
libtmux.test.constants: Test-related constants (TEST_SESSION_PREFIX, etc.)libtmux.test.environment: Environment variable mockinglibtmux.test.random: Random string generation utilitieslibtmux.test.temporary: Temporary session/window managementBreaking: Import paths have changed. Update imports:
# Old (0.44.x and earlier)
from libtmux.test import (
TEST_SESSION_PREFIX,
get_test_session_name,
get_test_window_name,
namer,
temp_session,
temp_window,
EnvironmentVarGuard,
)
# New (0.45.0+)
from libtmux.test.constants import TEST_SESSION_PREFIX
from libtmux.test.environment import EnvironmentVarGuard
from libtmux.test.random import get_test_session_name, get_test_window_name, namer
from libtmux.test.temporary import temp_session, temp_window
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.44.2...v0.45.0
libtmux 0.45.0 splits the historical libtmux.test module into focused helper
modules.
Import paths changed from the root libtmux.test module to
libtmux.test.constants, libtmux.test.environment,
libtmux.test.random, and libtmux.test.temporary.
# Old
from libtmux.test import TEST_SESSION_PREFIX, EnvironmentVarGuard, namer
# New
from libtmux.test.constants import TEST_SESSION_PREFIX
from libtmux.test.environment import EnvironmentVarGuard
from libtmux.test.random import namer
CI now includes a runtime-dependency check, inspired by @ppentchev's review comments on #572.
fix(typings) Move typing-extensions into TypeGuard by @tony in https://github.com/tmux-python/libtmux/pull/572
TypeGuard by @tony in https://github.com/tmux-python/libtmux/pull/572Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.44.1...v0.44.2
libtmux 0.44.2 fixes typing import behavior and cleans up version tests.
typing_extensions imports are guarded (#572)typing_extensions is now imported only when needed for type checking, fixing
runtime dependency issues. This continues the #564 work.
Version-related tests were consolidated into parametrized fixtures with named
cases, and the broken test_window_rename test was repaired.
types: Only use typing-extensions if necessary by @ppentchev in https://github.com/tmux-python/libtmux/pull/563
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.44.0...v0.44.1
libtmux 0.44.1 is a small packaging fix.
typing_extensions is optional at runtime (#563)Runtime imports now avoid typing_extensions unless the running Python needs
it. Thanks @ppentchev.
_by @tony in https://github.com/tmux-python/libtmux/pull/566_.
by @tony in https://github.com/tmux-python/libtmux/pull/566.
Added context manager support for all main tmux objects:
Server: Automatically kills the server when exiting the contextSession: Automatically kills the session when exiting the contextWindow: Automatically kills the window when exiting the contextPane: Automatically kills the pane when exiting the contextExample usage:
with Server() as server:
with server.new_session() as session:
with session.new_window() as window:
with window.split() as pane:
pane.send_keys('echo "Hello"')
# Do work with the pane
# Everything is cleaned up automatically when exiting contexts
This makes it easier to write clean, safe code that properly cleans up tmux resources.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.43.0...v0.44.0
libtmux 0.44.0 adds context-manager support for tmux objects.
{class}~libtmux.Server, {class}~libtmux.Session,
{class}~libtmux.Window, and {class}~libtmux.Pane implement context manager
protocols that kill the object on exit.
with Server() as server:
with server.new_session() as session:
with session.new_window() as window:
with window.split() as pane:
pane.send_keys('echo "Hello"')
TestServer: Server, but partial'd to run on a test socket by @tony in https://github.com/tmux-python/libtmux/pull/565
TestServer: Server, but partial'd to run on a test socket by @tony in https://github.com/tmux-python/libtmux/pull/565Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.42.1...v0.43.0
libtmux 0.43.0 improves server initialization and testing ergonomics.
{class}~libtmux.Server accepts socket_name_factory and on_init
callbacks, making it easier to create multiple servers with unique names and
track initialized instances.
TestServer creates isolated tmux servers (#565)The {fixture}TestServer fixture creates temporary tmux servers with unique
socket names, cleanup, tests, documentation, and doctest namespace support.
The topic docs gained fixed "Topics" links and more traversal guidance.
Move a typing-extensions import into a t.TYPE_CHECKING section by @ppentchev in https://github.com/tmux-python/libtmux/pull/562
typing-extensions usagetyping-extensions for older python versions by @tony in https://github.com/tmux-python/libtmux/pull/564Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.42.0...v0.42.1
libtmux 0.42.1 repairs a typing import edge case.
Self is imported only for type checking (#562)Tests now import Self behind TYPE_CHECKING, avoiding runtime dependency
issues. Thanks @ppentchev.
The testing and lint groups include typing-extensions for Python versions
that need it.
tmux_cmd: Modernize to use text=True by @tony in https://github.com/tmux-python/libtmux/pull/560
tmux_cmd: Modernize to use text=True by @tony in https://github.com/tmux-python/libtmux/pull/560
Attempted fix for https://github.com/tmux-python/libtmux/pull/558.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.41.0...v0.42.0
libtmux 0.42.0 removes the last Python 2 console conversion helpers.
tmux_cmd uses text-mode subprocess output (#560){func}~libtmux.common.tmux_cmd now uses text=True, deprecating
console_to_str() and str_from_console(). The compatibility helpers were
removed as Python 2-era artifacts. Fixes #558.
Fix hardcoded uid in __str__ method of Server class by @lazysegtree in https://github.com/tmux-python/libtmux/pull/557
__str__ method of Server class by @lazysegtree in https://github.com/tmux-python/libtmux/pull/557Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.40.1...v0.41.0
libtmux 0.41.0 fixes default-socket representation and continues annotation modernization.
Server.__repr__() uses the effective UID (#557){meth}~libtmux.Server.__repr__ now uses {func}os.geteuid when constructing
the default socket path. Fixes #556. Thanks @lazysegtree.
Server.colors docs list valid values (#544)The colors docstring now documents 88 and 256. Thanks @TravisDart.
All Python files now use from __future__ import annotations, and Ruff rules
UP006 / UP007 enforce modern annotation syntax.
Fix passing both window command and environment by @ppentchev in https://github.com/tmux-python/libtmux/pull/553
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.40.0...v0.40.1
libtmux 0.40.1 fixes environment propagation during session creation.
Server.new_session() handles environment variables correctly (#553){meth}~libtmux.Server.new_session now passes environment values to new tmux
sessions correctly. Thanks @ppentchev.
_Maintenance only, no bug fixes or new features_
Maintenance only, no bug fixes or new features
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.39.0...v0.40.0
libtmux 0.40.0 is a maintenance release with one naming break and broad lint cleanup.
_global was renamed to global_The keyword spelling now follows the Python-safe convention used elsewhere in the API.
The codebase was reformatted and lint-fixed with Ruff 0.8.4, including preview
and unsafe fixes, and legacy test_select_pane stability was improved (#552).
Drop Python 3.8 by @tony in https://github.com/tmux-python/libtmux/pull/548
Drop Python 3.8 by @tony in https://github.com/tmux-python/libtmux/pull/548
Python 3.8 reached end-of-life on October 7th, 2024 (see devguide.python.org, Status of Python Versions, Unsupported versions
See also: https://devguide.python.org/versions/#unsupported-versions
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.38.1...v0.39.0
libtmux 0.39.0 moves the Python floor forward.
Python 3.9 became the minimum for this release line. tmuxp 1.48.0 remained the last tmuxp release for Python 3.8.
Minimum Python back to 3.8 for now.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.38.0...v0.38.1
Minimum Python back to 3.8 for now.
libtmux 0.38.1 keeps Python 3.8 support temporarily.
The project held the Python floor at 3.8 for this patch release.
Project and package management: poetry to uv
Project and package management: poetry to uv (#547)
uv is the new package and project manager for the project, replacing Poetry.
Code quality: Use f-strings in more places (#540)
via ruff 0.4.2.
[docs] Sphinx v8 compatibility: configure a non-empty inventory name for Python Intersphinx mapping. by @jayaddison in https://github.com/tmux-python/libtmux/pull/542
Fix docstrings in query_list for MultipleObjectsReturned and
ObjectDoesNotExist.
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.37.0...v0.38.0
libtmux 0.38.0 changes the project management and build backend stack.
uv replaced Poetry for project and dependency management.
hatchling replaced Poetry as the build backend, following Python packaging guidance.
The docs for ObjectDoesNotExist and MultipleObjectsReturned in the query
list internals were corrected.
Ruff 0.4.2 applied more f-string modernization across the codebase.
pytest-xdist support in https://github.com/tmux-python/libtmux/pull/522
retry_until() tests: Relax clock in assert.tests/test_pane.py::test_capture_pane_start: Use retry_until() to poll, improve correctness of test.Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.36.0...v0.37.0
libtmux 0.37.0 is a test and maintenance release focused on parallel test runs and flaky-test cleanup.
pytest-xdist is available for parallel test runs.
$ py.test -n auto
pytest-watcher can also inherit the option:
$ env PYTEST_ADDOPTS='-n auto' make start
The release also relaxed timing-sensitive retry_until() assertions and made
test_capture_pane_start poll for the expected state.
Poetry moved from 1.8.1 to 1.8.2, and links that had previously been plain text are now automatically linkified.
Linting: Aggressive ruff pass (ruff v0.3.4) by @tony in https://github.com/tmux-python/libtmux/pull/539
ruff pass (ruff v0.3.4) by @tony in https://github.com/tmux-python/libtmux/pull/539Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.35.1...v0.36.0
libtmux 0.36.0 is an automated lint-cleanup release.
Ruff 0.3.4 was used to apply lint and format fixes across the codebase, including preview and unsafe fixes.
fix: server.attached_sessions by @patrislav1 in https://github.com/tmux-python/libtmux/pull/537
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.35.0...v0.35.1
libtmux 0.35.1 fixes attached-session detection.
Server.attached_sessions handles multiple clients (#537, #538){attr}~libtmux.Server.attached_sessions now reports attached sessions
correctly when multiple clients are attached. Thanks @patrislav1.
refactor: Eliminate redundant targets / window_index's across codebase by @tony in https://github.com/tmux-python/libtmux/pull/536
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.34.0...v0.35.0
libtmux 0.35.0 removes redundant target/index handling.
Internal command construction no longer carries duplicate target and
window_index values through the codebase.
Commands: All cmd() methods using custom or overridden targets must use the keyword argument target. This avoids entanglement with inner shell values
Commands: All cmd() methods using custom or overridden targets must use the keyword argument target. This avoids entanglement with inner shell values that include -t for other purposes. These methods include:
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.33.0...v0.34.0
libtmux 0.34.0 clarifies how custom command targets are passed.
cmd() target overrides are keyword-only (#535)All object-level cmd() methods require the target= keyword for custom or
overridden tmux targets. This avoids confusing tmux command targets with inner
shell arguments that may also include -t.
Affected methods include {meth}~libtmux.Server.cmd,
{meth}~libtmux.Session.cmd, {meth}~libtmux.Window.cmd, and
{meth}~libtmux.Pane.cmd.
Deprecate Window.split_window()
Session.new_window():
direction, via WindowDirection).Added {meth}Window.new_window() shorthand to create window based on that
window's position.
Window.split_window() to Window.split()
Window.split_window()Pane.split_window() to Pane.split()
Deprecate Pane.split_window()
Learned direction, via PaneDirection).
vertical and horizontal in favor of direction.Learned zoom
It's now possible to retrieve the position of a pane in a window via a bool helper::
poetry: 1.7.1 -> 1.8.1
See also: https://github.com/python-poetry/poetry/blob/1.8.1/CHANGELOG.md
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.32.0...v0.33.0b0
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.32.0...v0.33.0
libtmux 0.33.0 reshapes session/window creation and pane splitting around clearer direction objects and keyword-only calls.
{meth}~libtmux.Session.new_window learned direction via
{class}~libtmux.constants.WindowDirection, and
{meth}~libtmux.Window.new_window was added as a shorthand from an existing
window's position. Arguments after the window name are keyword-only following
PEP 3102.
{meth}~libtmux.Window.split_window is deprecated in favor of
{meth}~libtmux.Window.split, and pane splitting moves from vertical /
horizontal booleans to {class}~libtmux.constants.PaneDirection. The split
API also gained zoom.
{class}~libtmux.Pane exposes boolean helpers for pane position within a
window: {attr}~libtmux.Pane.at_left, {attr}~libtmux.Pane.at_right,
{attr}~libtmux.Pane.at_top, and {attr}~libtmux.Pane.at_bottom.
The development dependency manager was updated to Poetry 1.8.1.
libtmux 0.33.0 reshapes session/window creation and pane splitting around clearer direction objects and keyword-only calls.
new_window() learned direction via WindowDirection , and new_window() was added as a shorthand from an existing window’s position. Arguments after the window name are keyword-only following PEP 3102 .
split_window() is deprecated in favor of split() , and pane splitting moves from vertical / horizontal booleans to PaneDirection . The split API also gained zoom .
Pane exposes boolean helpers for pane position within a window: at_left , at_right , at_top , and at_bottom .
The development dependency manager was updated to Poetry 1.8.1.
Nothing published for this version
Split, new window follow ups by @tony in https://github.com/tmux-python/libtmux/pull/534
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.33.0b0...v0.33.0b1
Deprecate Window.split_window()
Session.new_window() to Session.new_window()
direction, via WindowDirection).Window.split_window() to Window.split()
Window.split_window()Pane.split_window() to Pane.split()
Deprecate Pane.split_window()
Learned direction, via PaneDirection).
vertical and horizontal in favor of direction.Learned zoom
It's now possible to retrieve the position of a pane in a window via a bool helper::
poetry: 1.7.1 -> 1.8.1
See also: https://github.com/python-poetry/poetry/blob/1.8.1/CHANGELOG.md
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.32.0...v0.33.0b0
Fix docstring ordering in pane.split_window by @Ngalstyan4 in https://github.com/tmux-python/libtmux/pull/528
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.31.0...v0.32.0
libtmux 0.32.0 is a packaging and tooling maintenance release.
Root package implicit imports were added to __init__.py thanks to @ssbarnea,
and Ruff moved from 0.2.2 to 0.3.0.
# Post-release Documentation Fixes - Doc fixes to command examples Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.31.0...v0.31.0pos
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.31.0...v0.31.0post0
Session.attached_window deprecated
Streamline {Server,Session,Window,Pane}.cmd(), across all usages to:
Server.attached_windows now users QueryList’s .filter()Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.30.2...v0.31.0
libtmux 0.31.0 cleans up command method signatures and renames attached-object properties to active-object properties.
cmd() methods were streamlined (#527){meth}~libtmux.Server.cmd, {meth}~libtmux.Session.cmd,
{meth}~libtmux.Window.cmd, and {meth}~libtmux.Pane.cmd now use the command
string as the first positional argument and drop unused keyword arguments.
Session.attached_window, Session.attached_pane, and Window.attached_pane
were renamed to {attr}~libtmux.Session.active_window,
{attr}~libtmux.Session.active_pane, and
{attr}~libtmux.Window.active_pane. The old names were deprecated.
README and quickstart content now document .cmd() usage, and the command
methods gained docstrings and doctests.
Server.attached_windows uses QueryList.filter() (#527)The implementation now relies on {meth}~libtmux.common.QueryList.filter.
Documentation-only updates followed the main release.
libtmux 0.31.0 cleans up command method signatures and renames attached-object properties to active-object properties.
cmd() , cmd() , cmd() , and cmd() now use the command string as the first positional argument and drop unused keyword arguments.
Session.attached_window , Session.attached_pane , and Window.attached_pane were renamed to active_window , active_pane , and active_pane . The old names were deprecated.
README and quickstart content now document .cmd() usage, and the command methods gained docstrings and doctests.
The implementation now relies on filter() .
Documentation-only updates followed the main release.
- TMUX_MAX_VERSION: 3.3 -> 3.4 Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.30.1...v0.30.2
TMUX_MAX_VERSION: 3.3 -> 3.4Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.30.1...v0.30.2
libtmux 0.30.2 updates known tmux version bounds.
TMUX_MAX_VERSION moved to 3.4The known tmux maximum version changed from 3.3 to 3.4.
pytest plugin, test module: Update to renamed methods introduced in v0.30.0
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.30.0...v0.30.1
libtmux 0.30.1 aligns tests with the 0.30.0 method renames.
The pytest plugin and test modules were updated to call the renamed 0.30.0 methods.
Deprecated Window.select_window()
by @tony in https://github.com/tmux-python/libtmux/pull/525
Pane.kill()Window.select_window() renamed to Window.select()
Window.select_window()Pane.select_pane() renamed to Pane.select()
Pane.pane_select()Session.attach_session() renamed to Session.attach()
Session.attach_session()Server.kill_server() renamed to Server.kill()
Server.kill_server()Session.kill_session() renamed to Session.kill()
Session.kill_session()Window.kill_window() renamed to Window.kill()
Window.kill_window()Server.new_session(): Support environment variables
Window.split_window(): Support size via -l
Supports columns/rows (size=10) and percentage (size='10%')
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.29.0...v0.30.0
libtmux 0.30.0 continues the modern object-method naming pass and adds pane kill support.
{meth}~libtmux.Pane.kill was added.
{meth}~libtmux.Server.new_session gained environment-variable support, and
{meth}~libtmux.Window.split_window learned size for row/column counts or
percentage values.
Window.select_window, Pane.select_pane, Session.attach_session,
Server.kill_server, Session.kill_session, and Window.kill_window were
renamed to their shorter modern methods and left behind deprecation warnings.
fix(warnings): Use DeprecationWarning for APIs being deprecated by @tony in https://github.com/tmux-python/libtmux/pull/526
DeprecationWarning for APIs being deprecated by @tony in https://github.com/tmux-python/libtmux/pull/526DeprecationWarning in tests @tony in https://github.com/tmux-python/libtmux/pull/526Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.28.1...v0.29.0
libtmux 0.29.0 prepares users for the method-renaming transition.
APIs scheduled for deprecation now emit {class}DeprecationWarning, and pytest
ignores those expected warnings by default.
_Maintenance only, no bug fixes or new features_
Maintenance only, no bug fixes or new features
Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.28.0...v0.28.1
libtmux 0.28.1 is maintenance for the 0.28 transition.
The docs and migration guide were updated for the 0.28.0 behavior changes.
GitHub Actions dependencies were bumped to Node 20-compatible versions.
0.28 +: Now _defaults_ to attach=False.
Session.new_window() + Window.split_window(): No longer attaches by defaultattach=False.attach=True.Pass attach=True for the old behavior.
Pane.resize_pane() renamed to Pane.resize(): (#523)This convention will be more consistent with Window.resize().
Pane.resize_pane(): Params changed (#523)-U, -D, -L, -R directly, instead accepts
ResizeAdjustmentDirection.Pane.resize(): Improved param coverage (#523)Learned to accept adjustments via adjustment_direction w/
ResizeAdjustmentDirection + adjustment.
Learned to accept manual height and / or width (columns/rows or percentage)
Zoom (and unzoom)
Window.resize_window(): New Method (#523)If Pane.resize_pane() (now Pane.resize()) didn't work before, try resizing the window.
Window.refresh() and Pane.refresh(): Refresh more underlying state (#523)Obj._refresh: Allow passing args (#523)e.g. -a (all) to list-panes and list-windows
Server.panes: Fix listing of panes (#523)Would list only panes in attached session, rather than all in a server.
Obj._refresh: Allow passing list_extra_args to ensure list-windows and
list-panes can return more than the target (#523)Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.27.1...v0.28.0
libtmux 0.28.0 changes default attachment behavior and modernizes pane/window resizing.
{meth}~libtmux.Session.new_window and
{meth}~libtmux.Window.split_window no longer attach/select by default.
Pass attach=True to keep the older behavior.
Pane.resize() (#523)Pane.resize_pane() was renamed to {meth}~libtmux.Pane.resize. Directional
flags now flow through {class}~libtmux.constants.ResizeAdjustmentDirection,
and height, width, and zoom controls are supported.
{meth}~libtmux.Window.resize was added. If pane resizing was ineffective in
older releases, resizing the window first may be the better approach.
{meth}~libtmux.Window.refresh, {meth}~libtmux.Pane.refresh, and the internal
refresh path now capture more state, and Server.panes lists panes across the
server instead of only the attached session.
The test matrix gained tmux 3.4, and pytest fixture warnings were fixed (#519).
pyproject: Include MIGRATION in sdist by @tony in https://github.com/tmux-python/libtmux/pull/517, for https://github.com/tmux-python/libtmux/issues/5
MIGRATION in sdist by @tony in https://github.com/tmux-python/libtmux/pull/517, for https://github.com/tmux-python/libtmux/issues/508Full Changelog: https://github.com/tmux-python/libtmux/compare/v0.27.0...v0.27.1
libtmux 0.27.1 repairs source distribution contents.
MIGRATION is included in sdists (#517)The source distribution now includes the migration guide needed by downstream packagers. Related to #508.
Your coding agent can read these notes before it upgrades. Set up the MCP server →