Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1df438a
ci(tests): remove tmux < 3.2 from test matrix
tony Nov 28, 2025
d35e59f
common.py(refactor): Bump TMUX_MIN_VERSION to 3.2a
tony Nov 29, 2025
1351964
server.py(refactor): Remove pre-3.2 version guards
tony Nov 29, 2025
1350cb9
session.py(refactor): Remove pre-3.2 version guards
tony Nov 29, 2025
fb48dce
window.py(refactor): Remove pre-3.2 version guards
tony Nov 29, 2025
b4e8d20
pane.py(refactor): Remove pre-3.2 version guards
tony Nov 29, 2025
cf66ebf
tests/test_server.py(refactor): Remove pre-3.2 version conditionals
tony Nov 29, 2025
701915b
tests/test_session.py(refactor): Remove pre-3.2 version conditionals
tony Nov 29, 2025
0139bca
tests/test_window.py(refactor): Remove pre-3.2 version conditionals
tony Nov 29, 2025
3446043
tests/test_pane.py(refactor): Remove pre-3.2 version conditionals
tony Nov 29, 2025
acd91cf
tests/legacy_api(refactor): Remove pre-3.2 version conditionals
tony Nov 29, 2025
1863329
docs/quickstart.md(refactor): Update tmux requirements
tony Nov 29, 2025
1ba834a
README.md(refactor): Update backports documentation
tony Nov 29, 2025
39f7815
docs(refactor): Remove outdated tmux version references
tony Nov 29, 2025
07ba797
CHANGES(docs): Document tmux < 3.2 support removal
tony Nov 29, 2025
858329d
tests/legacy_api/test_server.py(refactor): Remove pre-3.2 version con…
tony Nov 29, 2025
59e6ede
common.py(docs): Update version examples in docstrings
tony Nov 29, 2025
174341b
pane.py(docs): Remove outdated tmux version comment
tony Nov 29, 2025
a145e7f
exc.py(docs): Remove version reference from InvalidOption docstring
tony Nov 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
python-version: ['3.14']
tmux-version: ['2.6', '2.7', '2.8', '3.0a', '3.1b', '3.2a', '3.3a', '3.4', '3.5', '3.6', 'master']
tmux-version: ['3.2a', '3.3a', '3.4', '3.5', '3.6', 'master']
steps:
- uses: actions/checkout@v5

Expand Down
12 changes: 11 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,17 @@ $ uvx --from 'libtmux' --prerelease allow python

<!-- To maintainers and contributors: Please add notes for the forthcoming version below -->

_Future release notes will be placed here_
### Breaking Changes

#### tmux 1.8 to 3.1c support removed (#608)

Support for tmux versions below 3.2a has been removed. This completes the
deprecation announced in v0.48.0.

- Minimum tmux version is now 3.2a (`TMUX_MIN_VERSION`)
- Removed `TMUX_SOFT_MIN_VERSION` constant and deprecation warning system
- Removed version guards throughout the codebase
- For users on older tmux, use libtmux v0.48.x

## libtmux 0.48.0 (2025-11-28)

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,14 +246,14 @@ Window(@1 1:..., Session($1 ...))
Session($1 ...)
```

# Python support
# Backports

Unsupported / no security releases or bug fixes:

- tmux < 3.2: The backports branch is
[`v0.48.x`](https://github.com/tmux-python/libtmux/tree/v0.48.x).
- Python 2.x: The backports branch is
[`v0.8.x`](https://github.com/tmux-python/libtmux/tree/v0.8.x).
- tmux 1.8 to 3.1c: The backports branch is
[`v0.48.x`](https://github.com/tmux-python/libtmux/tree/v0.48.x).

# Donations

Expand Down
3 changes: 1 addition & 2 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ from inside a live tmux session.

## Requirements

- [tmux] 3.2a or newer (recommended)
- tmux 1.8 - 3.1 are deprecated and will be unsupported in a future release
- [tmux] 3.2a or newer
- [pip] - for this handbook's examples

[tmux]: https://tmux.github.io/
Expand Down
65 changes: 14 additions & 51 deletions src/libtmux/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,49 +24,16 @@


#: Minimum version of tmux required to run libtmux
TMUX_MIN_VERSION = "1.8"
TMUX_MIN_VERSION = "3.2a"

#: Most recent version of tmux supported
TMUX_MAX_VERSION = "3.6"

#: Minimum version before deprecation warning is shown
TMUX_SOFT_MIN_VERSION = "3.2a"

SessionDict = dict[str, t.Any]
WindowDict = dict[str, t.Any]
WindowOptionDict = dict[str, t.Any]
PaneDict = dict[str, t.Any]

#: Flag to ensure deprecation warning is only shown once per process
_version_deprecation_checked: bool = False


def _check_deprecated_version(version: LooseVersion) -> None:
"""Check if tmux version is deprecated and warn once.

This is called from get_version() on first invocation.
"""
global _version_deprecation_checked
if _version_deprecation_checked:
return
_version_deprecation_checked = True

import os
import warnings

if os.environ.get("LIBTMUX_SUPPRESS_VERSION_WARNING"):
return

if version < LooseVersion(TMUX_SOFT_MIN_VERSION):
warnings.warn(
f"tmux {version} is deprecated and will be unsupported in a future "
f"libtmux release. Please upgrade to tmux {TMUX_SOFT_MIN_VERSION} "
"or newer. Set LIBTMUX_SUPPRESS_VERSION_WARNING=1 to suppress this "
"warning.",
FutureWarning,
stacklevel=4,
)


class EnvironmentMixin:
"""Mixin for manager session and server level environment variables in tmux."""
Expand Down Expand Up @@ -321,7 +288,7 @@ def get_version() -> LooseVersion:
return LooseVersion(f"{TMUX_MAX_VERSION}-openbsd")
msg = (
f"libtmux supports tmux {TMUX_MIN_VERSION} and greater. This system"
" is running tmux 1.3 or earlier."
" does not meet the minimum tmux version requirement."
)
raise exc.LibTmuxException(
msg,
Expand All @@ -336,9 +303,7 @@ def get_version() -> LooseVersion:

version = re.sub(r"[a-z-]", "", version)

version_obj = LooseVersion(version)
_check_deprecated_version(version_obj)
return version_obj
return LooseVersion(version)


def has_version(version: str) -> bool:
Expand All @@ -347,7 +312,7 @@ def has_version(version: str) -> bool:
Parameters
----------
version : str
version number, e.g. '1.8'
version number, e.g. '3.2a'

Returns
-------
Expand All @@ -363,7 +328,7 @@ def has_gt_version(min_version: str) -> bool:
Parameters
----------
min_version : str
tmux version, e.g. '1.8'
tmux version, e.g. '3.2a'

Returns
-------
Expand All @@ -379,7 +344,7 @@ def has_gte_version(min_version: str) -> bool:
Parameters
----------
min_version : str
tmux version, e.g. '1.8'
tmux version, e.g. '3.2a'

Returns
-------
Expand All @@ -395,7 +360,7 @@ def has_lte_version(max_version: str) -> bool:
Parameters
----------
max_version : str
tmux version, e.g. '1.8'
tmux version, e.g. '3.2a'

Returns
-------
Expand All @@ -411,7 +376,7 @@ def has_lt_version(max_version: str) -> bool:
Parameters
----------
max_version : str
tmux version, e.g. '1.8'
tmux version, e.g. '3.2a'

Returns
-------
Expand All @@ -422,7 +387,7 @@ def has_lt_version(max_version: str) -> bool:


def has_minimum_version(raises: bool = True) -> bool:
"""Return True if tmux meets version requirement. Version >1.8 or above.
"""Return True if tmux meets version requirement. Version >= 3.2a.

Parameters
----------
Expand All @@ -441,6 +406,9 @@ def has_minimum_version(raises: bool = True) -> bool:

Notes
-----
.. versionchanged:: 0.49.0
Minimum version bumped to 3.2a. For older tmux, use libtmux v0.48.x.

.. versionchanged:: 0.7.0
No longer returns version, returns True or False

Expand All @@ -454,7 +422,7 @@ def has_minimum_version(raises: bool = True) -> bool:
msg = (
f"libtmux only supports tmux {TMUX_MIN_VERSION} and greater. This "
f"system has {get_version()} installed. Upgrade your tmux to use "
"libtmux."
"libtmux, or use libtmux v0.48.x for older tmux versions."
)
raise exc.VersionTooLow(msg)
return False
Expand Down Expand Up @@ -488,17 +456,12 @@ def session_check_name(session_name: str | None) -> None:
def handle_option_error(error: str) -> type[exc.OptionError]:
"""Raise exception if error in option command found.

In tmux 3.0, show-option and show-window-option return invalid option instead of
unknown option. See https://github.com/tmux/tmux/blob/3.0/cmd-show-options.c.

In tmux >2.4, there are 3 different types of option errors:
There are 3 different types of option errors:

- unknown option
- invalid option
- ambiguous option

In tmux <2.4, unknown option was the only option.

All errors raised will have the base error of :exc:`exc.OptionError`. So to
catch any option error, use ``except exc.OptionError``.

Expand Down
2 changes: 1 addition & 1 deletion src/libtmux/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def __init__(self, *args: object) -> None:


class InvalidOption(OptionError):
"""Option invalid to tmux, introduced in tmux v2.4."""
"""Option invalid to tmux."""


class AmbiguousOption(OptionError):
Expand Down
13 changes: 0 additions & 13 deletions src/libtmux/neo.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@
OutputsRaw = list[OutputRaw]


"""
Quirks:
QUIRK_TMUX_3_1_X_0001:
- tmux 3.1 and 3.1a:
- server crash with list-panes w/ buffer_created, client_activity, client_created
"""


@dataclasses.dataclass()
class Obj:
"""Dataclass of generic tmux object."""
Expand All @@ -43,14 +33,11 @@ class Obj:
active_window_index: str | None = None
alternate_saved_x: str | None = None
alternate_saved_y: str | None = None
# See QUIRK_TMUX_3_1_X_0001
buffer_name: str | None = None
buffer_sample: str | None = None
buffer_size: str | None = None
# See QUIRK_TMUX_3_1_X_0001
client_cell_height: str | None = None
client_cell_width: str | None = None
# See QUIRK_TMUX_3_1_X_0001
client_discarded: str | None = None
client_flags: str | None = None
client_height: str | None = None
Expand Down
51 changes: 18 additions & 33 deletions src/libtmux/pane.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import warnings

from libtmux import exc
from libtmux.common import has_gte_version, has_lt_version, tmux_cmd
from libtmux.common import tmux_cmd
from libtmux.constants import (
PANE_DIRECTION_FLAG_MAP,
RESIZE_ADJUSTMENT_DIRECTION_FLAG_MAP,
Expand Down Expand Up @@ -275,20 +275,21 @@ def resize(
elif height or width:
# Manual resizing
if height:
if isinstance(height, str):
if height.endswith("%") and not has_gte_version("3.1"):
raise exc.VersionTooLow
if not height.isdigit() and not height.endswith("%"):
raise exc.RequiresDigitOrPercentage
if (
isinstance(height, str)
and not height.isdigit()
and not height.endswith("%")
):
raise exc.RequiresDigitOrPercentage
tmux_args += (f"-y{height}",)

if width:
if isinstance(width, str):
if width.endswith("%") and not has_gte_version("3.1"):
raise exc.VersionTooLow
if not width.isdigit() and not width.endswith("%"):
raise exc.RequiresDigitOrPercentage

if (
isinstance(width, str)
and not width.isdigit()
and not width.endswith("%")
):
raise exc.RequiresDigitOrPercentage
tmux_args += (f"-x{width}",)
elif zoom:
# Zoom / Unzoom
Expand Down Expand Up @@ -585,7 +586,7 @@ def split(
size: int, optional
Cell/row or percentage to occupy with respect to current window.
environment: dict, optional
Environmental variables for new pane. tmux 3.0+ only. Passthrough to ``-e``.
Environmental variables for new pane. Passthrough to ``-e``.

Examples
--------
Expand Down Expand Up @@ -650,16 +651,7 @@ def split(
tmux_args += tuple(PANE_DIRECTION_FLAG_MAP[PaneDirection.Below])

if size is not None:
if has_lt_version("3.1"):
if isinstance(size, str) and size.endswith("%"):
tmux_args += (f"-p{str(size).rstrip('%')}",)
else:
warnings.warn(
'Ignored size. Use percent in tmux < 3.1, e.g. "size=50%"',
stacklevel=2,
)
else:
tmux_args += (f"-l{size}",)
tmux_args += (f"-l{size}",)

if full_window_split:
tmux_args += ("-f",)
Expand All @@ -670,28 +662,21 @@ def split(
tmux_args += ("-P", "-F{}".format("".join(tmux_formats))) # output

if start_directory:
# as of 2014-02-08 tmux 1.9-dev doesn't expand ~ in new-window -c.
start_path = pathlib.Path(start_directory).expanduser()
tmux_args += (f"-c{start_path}",)

if not attach:
tmux_args += ("-d",)

if environment:
if has_gte_version("3.0"):
for k, v in environment.items():
tmux_args += (f"-e{k}={v}",)
else:
logger.warning(
"Environment flag ignored, tmux 3.0 or newer required.",
)
for k, v in environment.items():
tmux_args += (f"-e{k}={v}",)

if shell:
tmux_args += (shell,)

pane_cmd = self.cmd("split-window", *tmux_args, target=target)

# tmux < 1.7. This is added in 1.7.
if pane_cmd.stderr:
if "pane too small" in pane_cmd.stderr:
raise exc.LibTmuxException(pane_cmd.stderr)
Expand Down Expand Up @@ -893,7 +878,7 @@ def split_window(
percent: int, optional
percentage to occupy with respect to current pane
environment: dict, optional
Environmental variables for new pane. tmux 3.0+ only. Passthrough to ``-e``.
Environmental variables for new pane. Passthrough to ``-e``.

Notes
-----
Expand Down
Loading