Options¶

Helpers for tmux options.

Option parsing function trade testability and clarity for performance.

Tmux options¶

Options in tmux consist of empty values, strings, integers, arrays, and complex shapes.

Marshalling types from text:

Integers: buffer-limit 50 to {'buffer-limit': 50} Booleans: exit-unattached on to {'exit-unattached': True}

Exploding arrays:

command-alias[1] split-pane=split-window to {'command-alias[1]': {'split-pane=split-window'}}

However, there is no equivalent to the above type of object in Python (a sparse array), so a SparseArray is used.

Exploding complex shapes:

"choose-session=choose-tree -s" to {'choose-session': 'choose-tree -s'}

Finally, we need to convert hyphenated keys to underscored attribute names and assign values, as python does not allow hyphens in attribute names.

command-alias is command_alias in python.

Options object¶

Dataclasses are used to provide typed access to tmux’ option shape.

Extra data gleaned from the options, such as user options (custom data) and an option being inherited,

User options¶

There are also custom user options, preceded with @, which exist are stored to Options.context.user_options as a dictionary.

> tmux set-option -w my-custom-variable my-value invalid option: my-custom-option

> tmux set-option -w @my-custom-option my-value > tmux show-option -w @my-custom-optione my-value

Inherited options¶

tmux show-options -A can include inherited options. The raw output of an inherited option is detected by the key having a *:

` visual-activity* on visual-bell* off `

A list of options that are inherited is kept at Options.context._inherited_options and Options.context.inherited_options.

They are mixed with the normal options, to differentiate them, run show_options() without include_inherited=True.

libtmux.options.handle_option_error(error)[source]¶

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:

  • 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.OptionError. So to catch any option error, use except exc.OptionError.

Parameters:

error (str) – Error response from subprocess call.

Raises:
Return type:

type[OptionError]

Examples

>>> result = server.cmd(
...     'set-option',
...     'unknown-option-name',
... )
>>> bool(isinstance(result.stderr, list) and len(result.stderr))
True
>>> import pytest
>>> from libtmux import exc
>>> with pytest.raises(exc.OptionError):
...     handle_option_error(result.stderr[0])
Return type:

type[OptionError]

Parameters:

error (str)

libtmux.options.convert_value(value)[source]¶

Convert raw option strings to python types.

Return type:

Union[str, int, bool, None, TypeVar(_V)]

Parameters:

value (_V | None)

Examples

>>> convert_value("on")
True
>>> convert_value("off")
False
>>> convert_value("1")
1
>>> convert_value("50")
50
>>> convert_value("%50")
'%50'
libtmux.options.convert_values(value)[source]¶

Recursively convert values to python types via convert_value().

Return type:

Union[str, int, bool, None, list[str | int | bool | None], dict[str, str | int | bool | None], SparseArray[str | int | bool | None], TypeVar(_V)]

Parameters:

value (_V | None)

>>> convert_values(None)
>>> convert_values("on")
True
>>> convert_values("off")
False
>>> convert_values(["on"])
[True]
>>> convert_values(["off"])
[False]
>>> convert_values({"window_index": "1"})
{'window_index': 1}
>>> convert_values({"visual-bell": "on"})
{'visual-bell': True}
libtmux.options.parse_options_to_dict(stdout)[source]¶

Process subprocess.stdout options or hook output to flat, naive, untyped dict.

Does not explode arrays or deep values.

Return type:

dict[str, str | None]

Parameters:

stdout (IO[str])

Examples

>>> import io
>>> raw_options = io.StringIO("status-keys vi")
>>> parse_options_to_dict(raw_options) == {"status-keys": "vi"}
True
>>> int_options = io.StringIO("message-limit 50")
>>> parse_options_to_dict(int_options) == {"message-limit": "50"}
True
>>> empty_option = io.StringIO("user-keys")
>>> parse_options_to_dict(empty_option) == {"user-keys": None}
True
>>> array_option = io.StringIO("command-alias[0] split-pane=split-window")
>>> parse_options_to_dict(array_option) == {
... "command-alias[0]": "split-pane=split-window"}
True
>>> array_option = io.StringIO("command-alias[40] split-pane=split-window")
>>> parse_options_to_dict(array_option) == {
... "command-alias[40]": "split-pane=split-window"}
True
>>> many_options = io.StringIO(r'''status-keys
... command-alias[0] split-pane=split-window
... ''')
>>> parse_options_to_dict(many_options) == {
... "command-alias[0]": "split-pane=split-window",
... "status-keys": None,}
True
>>> many_more_options = io.StringIO(r'''
... terminal-features[0] xterm*:clipboard:ccolour:cstyle:focus
... terminal-features[1] screen*:title
... ''')
>>> parse_options_to_dict(many_more_options) == {
... "terminal-features[0]": "xterm*:clipboard:ccolour:cstyle:focus",
... "terminal-features[1]": "screen*:title",}
True
>>> quoted_option = io.StringIO(r'''
... command-alias[0] "choose-session=choose-tree -s"
... ''')
>>> parse_options_to_dict(quoted_option) == {
... "command-alias[0]": "choose-session=choose-tree -s",
... }
True
libtmux.options.explode_arrays(_dict, force_array=False)[source]¶

Explode flat, naive options dict’s option arrays.

Return type:

dict[str, str | int | list[str] | dict[str, list[str]]]

Parameters:

Examples

>>> import io
>>> many_more_options = io.StringIO(r'''
... terminal-features[0] xterm*:clipboard:ccolour:cstyle:focus
... terminal-features[1] screen*:title
... ''')
>>> many_more_flat_dict = parse_options_to_dict(many_more_options)
>>> many_more_flat_dict == {
... "terminal-features[0]": "xterm*:clipboard:ccolour:cstyle:focus",
... "terminal-features[1]": "screen*:title",}
True
>>> explode_arrays(many_more_flat_dict) == {
... "terminal-features": {0: "xterm*:clipboard:ccolour:cstyle:focus",
... 1: "screen*:title"}}
True

tmux arrays allow non-sequential indexes, so we need to support that:

>>> explode_arrays(parse_options_to_dict(io.StringIO(r'''
... terminal-features[0] xterm*:clipboard:ccolour:cstyle:focus
... terminal-features[5] screen*:title
... '''))) == {
... "terminal-features": {0: "xterm*:clipboard:ccolour:cstyle:focus",
... 5: "screen*:title"}}
True

Use force_array=True for hooks, which always use array format:

>>> from libtmux._internal.sparse_array import SparseArray
>>> hooks_output = io.StringIO(r'''
... session-renamed[0] display-message 'renamed'
... session-renamed[5] refresh-client
... pane-focus-in[0] run-shell 'echo focus'
... ''')
>>> hooks_exploded = explode_arrays(
...     parse_options_to_dict(hooks_output),
...     force_array=True,
... )

Each hook becomes a SparseArray preserving indices:

>>> isinstance(hooks_exploded["session-renamed"], SparseArray)
True
>>> hooks_exploded["session-renamed"][0]
"display-message 'renamed'"
>>> hooks_exploded["session-renamed"][5]
'refresh-client'
>>> sorted(hooks_exploded["session-renamed"].keys())
[0, 5]
libtmux.options.explode_complex(_dict)[source]¶

Explode arrayed option’s complex values.

Return type:

dict[str, str | int | list[str | int] | dict[str, list[str | int]] | SparseArray[str | int] | None]

Parameters:

_dict (dict[str, str | int | list[str] | dict[str, list[str]]])

Examples

>>> import io
>>> explode_complex(explode_arrays(parse_options_to_dict(io.StringIO(r'''
... terminal-features[0] xterm*:clipboard:ccolour:cstyle:focus
... terminal-features[5] screen*:title
... '''))))
{'terminal-features': {'xterm*': ['clipboard', 'ccolour', 'cstyle', 'focus'], 'screen*': ['title']}}
>>> explode_complex(explode_arrays(parse_options_to_dict(io.StringIO(r'''
... terminal-features[0] xterm*:clipboard:ccolour:cstyle:focus
... terminal-features[5] screen*:title
... ''')))) == {
... "terminal-features": {"xterm*": ["clipboard", "ccolour", "cstyle", "focus"],
... "screen*": ["title"]}}
True
>>> explode_complex(explode_arrays(parse_options_to_dict(io.StringIO(r'''
... command-alias[0] split-pane=split-window
... command-alias[1] splitp=split-window
... command-alias[2] "server-info=show-messages -JT"
... ''')))) == {
... "command-alias": {"split-pane": "split-window",
... "splitp": "split-window",
... "server-info": "show-messages -JT"}}
True
>>> explode_complex(explode_arrays({"terminal-features": {0: "xterm*:clipboard:ccolour:cstyle:focus",
... 1: "screen*:title"}}))
{'terminal-features': {0: 'xterm*:clipboard:ccolour:cstyle:focus', 1: 'screen*:title'}}
>>> explode_complex(explode_arrays({"terminal-features": {0: "xterm*:clipboard:ccolour:cstyle:focus",
... 8: "screen*:title"}})) == SparseArray({'terminal-features': {0:
... 'xterm*:clipboard:ccolour:cstyle:focus', 8: 'screen*:title'}})
True
>>> explode_complex(explode_arrays(parse_options_to_dict(io.StringIO(r'''
... terminal-overrides[0] xterm-256color:Tc
... terminal-overrides[1] *:U8=0
... ''')))) == {
... "terminal-overrides": {"xterm-256color": {"Tc": None},
... "*": {"U8": 0}}}
True
>>> explode_complex(explode_arrays(parse_options_to_dict(io.StringIO(r'''
... user-keys[100] "\e[test"
... user-keys[6] "\e\n"
... user-keys[0] "\e[5;30012~"
... ''')))) == {
... "user-keys": {0: "\\e[5;30012~",
... 6: "\\e\\n",
... 100: "\\e[test"}}
True
>>> explode_complex(explode_arrays(parse_options_to_dict(io.StringIO(r'''
... status-format[0] "#[align=left range=left #{E:status-left-style}]#[push-default]#{T;=/#{status-left-length}:status-left}#[pop-default]#[norange default]#[list=on align=#{status-justify}]#[list=left-marker]<#[list=right-marker]>#[list=on]#{W:#[range=window|#{window_index} #{E:window-status-style}#{?#{&&:#{window_last_flag},#{!=:#{E:window-status-last-style},default}}, #{E:window-status-last-style},}#{?#{&&:#{window_bell_flag},#{!=:#{E:window-status-bell-style},default}}, #{E:window-status-bell-style},#{?#{&&:#{||:#{window_activity_flag},#{window_silence_flag}},#{!=:#{E:window-status-activity-style},default}}, #{E:window-status-activity-style},}}]#[push-default]#{T:window-status-format}#[pop-default]#[norange default]#{?window_end_flag,,#{window-status-separator}},#[range=window|#{window_index} list=focus #{?#{!=:#{E:window-status-current-style},default},#{E:window-status-current-style},#{E:window-status-style}}#{?#{&&:#{window_last_flag},#{!=:#{E:window-status-last-style},default}}, #{E:window-status-last-style},}#{?#{&&:#{window_bell_flag},#{!=:#{E:window-status-bell-style},default}}, #{E:window-status-bell-style},#{?#{&&:#{||:#{window_activity_flag},#{window_silence_flag}},#{!=:#{E:window-status-activity-style},default}}, #{E:window-status-activity-style},}}]#[push-default]#{T:window-status-current-format}#[pop-default]#[norange list=on default]#{?window_end_flag,,#{window-status-separator}}}#[nolist align=right range=right #{E:status-right-style}]#[push-default]#{T;=/#{status-right-length}:status-right}#[pop-default]#[norange default]"
... status-format[1] "#[align=centre]#{P:#{?pane_active,#[reverse],}#{pane_index}[#{pane_width}x#{pane_height}]#[default] }"
... ''')))) == {
... "status-format": {0: "#[align=left range=left #{E:status-left-style}]#[push-default]#{T;=/#{status-left-length}:status-left}#[pop-default]#[norange default]#[list=on align=#{status-justify}]#[list=left-marker]<#[list=right-marker]>#[list=on]#{W:#[range=window|#{window_index} #{E:window-status-style}#{?#{&&:#{window_last_flag},#{!=:#{E:window-status-last-style},default}}, #{E:window-status-last-style},}#{?#{&&:#{window_bell_flag},#{!=:#{E:window-status-bell-style},default}}, #{E:window-status-bell-style},#{?#{&&:#{||:#{window_activity_flag},#{window_silence_flag}},#{!=:#{E:window-status-activity-style},default}}, #{E:window-status-activity-style},}}]#[push-default]#{T:window-status-format}#[pop-default]#[norange default]#{?window_end_flag,,#{window-status-separator}},#[range=window|#{window_index} list=focus #{?#{!=:#{E:window-status-current-style},default},#{E:window-status-current-style},#{E:window-status-style}}#{?#{&&:#{window_last_flag},#{!=:#{E:window-status-last-style},default}}, #{E:window-status-last-style},}#{?#{&&:#{window_bell_flag},#{!=:#{E:window-status-bell-style},default}}, #{E:window-status-bell-style},#{?#{&&:#{||:#{window_activity_flag},#{window_silence_flag}},#{!=:#{E:window-status-activity-style},default}}, #{E:window-status-activity-style},}}]#[push-default]#{T:window-status-current-format}#[pop-default]#[norange list=on default]#{?window_end_flag,,#{window-status-separator}}}#[nolist align=right range=right #{E:status-right-style}]#[push-default]#{T;=/#{status-right-length}:status-right}#[pop-default]#[norange default]",
... 1: "#[align=centre]#{P:#{?pane_active,#[reverse],}#{pane_index}[#{pane_width}x#{pane_height}]#[default] }",
... }}
True
class libtmux.options.OptionsMixin[source]¶

Mixin for managing tmux options based on scope.

When not a user (custom) option, scope can be implied.

__init__(default_option_scope=None)[source]¶

When not a user (custom) option, scope can be implied.

Parameters:

default_option_scope (OptionScope | None)

Return type:

None

set_option(option, value, _format=None, prevent_overwrite=None, ignore_errors=None, suppress_warnings=None, append=None, g=None, global_=None, scope=<libtmux.constants._DefaultOptionScope object>)[source]¶

Set option for tmux target.

Wraps $ tmux set-option <option> <value>.

Return type:

Self

Parameters:
  • option (str) – option to set, e.g. ‘aggressive-resize’

  • value (str) – option value. True/False will turn in ‘on’ and ‘off’, also accepts string of ‘on’ or ‘off’ directly.

  • deprecated: (..) – 0.28: Deprecated by g for global, use global_` instead.

  • _format (bool | None)

  • prevent_overwrite (bool | None)

  • ignore_errors (bool | None)

  • suppress_warnings (bool | None)

  • append (bool | None)

  • g (bool | None)

  • global_ (bool | None)

  • scope (OptionScope | _DefaultOptionScope | None)

Raises:

Examples

>>> import typing as t
>>> from libtmux.common import tmux_cmd
>>> from libtmux.constants import OptionScope
>>> from libtmux._internal.sparse_array import SparseArray
>>> class MyServer(OptionsMixin):
...     socket_name = server.socket_name
...     def cmd(self, cmd: str, *args: object):
...         cmd_args: t.List[t.Union[str, int]] = [cmd]
...         if self.socket_name:
...             cmd_args.insert(0, f"-L{self.socket_name}")
...         cmd_args.insert(0, "-f/dev/null")
...         return tmux_cmd(*cmd_args, *args)
...
...     default_option_scope = OptionScope.Server
>>> MyServer().set_option('escape-time', 1250)
<libtmux.options.MyServer object at ...>
>>> MyServer()._show_option('escape-time')
1250
>>> MyServer().set_option('escape-time', 495)
<libtmux.options.MyServer object at ...>
>>> MyServer()._show_option('escape-time')
495
unset_option(option, unset_panes=None, global_=None, ignore_errors=None, scope=<libtmux.constants._DefaultOptionScope object>)[source]¶

Unset option for tmux target.

Wraps $ tmux set-option -u <option> / $ tmux set-option -U <option>

Return type:

Self

Parameters:
  • option (str) – option to unset, e.g. ‘aggressive-resize’

  • unset_panes (bool | None)

  • global_ (bool | None)

  • ignore_errors (bool | None)

  • scope (OptionScope | _DefaultOptionScope | None)

Raises:

Examples

>>> import typing as t
>>> from libtmux.common import tmux_cmd
>>> from libtmux.constants import OptionScope
>>> class MyServer(OptionsMixin):
...     socket_name = server.socket_name
...     def cmd(self, cmd: str, *args: object):
...         cmd_args: t.List[t.Union[str, int]] = [cmd]
...         if self.socket_name:
...             cmd_args.insert(0, f"-L{self.socket_name}")
...         cmd_args.insert(0, "-f/dev/null")
...         return tmux_cmd(*cmd_args, *args)
...
...     default_option_scope = OptionScope.Server
>>> MyServer().set_option('escape-time', 1250)
<libtmux.options.MyServer object at ...>
>>> MyServer()._show_option('escape-time')
1250
>>> MyServer().unset_option('escape-time')
<libtmux.options.MyServer object at ...>
>>> isinstance(MyServer()._show_option('escape-time'), int)
True
show_options(global_=False, scope=<libtmux.constants._DefaultOptionScope object>, include_hooks=None, include_inherited=None)[source]¶

Return all options for the target.

Return type:

dict[str, str | int | list[str | int] | dict[str, list[str | int]] | SparseArray[str | int] | None]

Parameters:
  • global (bool, optional) – Pass -g flag for global options, default False.

  • scope (OptionScope | _DefaultOptionScope | None, optional) – Option scope (Server/Session/Window/Pane), defaults to object’s scope.

  • include_hooks (bool, optional) – Include hook options (-H flag).

  • include_inherited (bool, optional) – Include inherited options (-A flag).

  • global_ (bool)

Returns:

Dictionary with all options, arrays exploded and values converted.

Return type:

ExplodedComplexUntypedOptionsDict

Raises:

Examples

>>> options = server.show_options()
>>> isinstance(options, dict)
True
>>> 'buffer-limit' in options
True
show_option(option, global_=False, g=False, scope=<libtmux.constants._DefaultOptionScope object>, ignore_errors=None, include_hooks=None, include_inherited=None)[source]¶

Return option value for the target.

Return type:

Any | None

Parameters:
  • option (str)

  • g (bool, optional) – Pass -g flag, global. Default False.

  • global_ (bool)

  • scope (OptionScope | _DefaultOptionScope | None)

  • ignore_errors (bool | None)

  • include_hooks (bool | None)

  • include_inherited (bool | None)

Raises:

Examples

>>> import typing as t
>>> from libtmux.common import tmux_cmd
>>> from libtmux.constants import OptionScope
>>> class MyServer(OptionsMixin):
...     socket_name = server.socket_name
...     def cmd(self, cmd: str, *args: object):
...         cmd_args: t.List[t.Union[str, int]] = [cmd]
...         if self.socket_name:
...             cmd_args.insert(0, f"-L{self.socket_name}")
...         cmd_args.insert(0, "-f/dev/null")
...         return tmux_cmd(*cmd_args, *args)
...
...     default_option_scope = OptionScope.Server
>>> MyServer().cmd('new-session', '-d')
<libtmux.common.tmux_cmd object at ...>
>>> MyServer().show_option('exit-unattached', global_=True)
False