Filed against tmux-python/libtmux v0.62.0, tmux 3.6a.
What happens
convert_value applies tmux's built-in option coercion to every value, and the call sites reach user options too:
src/libtmux/options.py#L245 — convert_values, dict branch
src/libtmux/options.py#L1216 — _show_option, direct lookup
tmux(1) defines a user option as carrying an arbitrary string:
tmux also supports user options which are prefixed with a '@'. User options may have any name, so long as they are prefixed with '@', and be set to any string.
So a user option does not survive the round trip when its value happens to read as a tmux boolean or a number:
| set |
show_option returns |
type |
123 |
123 |
int |
on |
True |
bool |
off |
False |
bool |
true |
'true' |
str |
3.5 |
'3.5' |
str |
show_options() returns the same converted values.
The coercion is right for built-in options — tmux really does use on/off there. For a @ name it is also not self-consistent: on converts but true does not, 123 converts but 3.5 does not.
Repro
import libtmux
server = libtmux.Server(socket_name="probe")
pane = server.new_session("probe").windows[0].panes[0]
for raw in ("123", "on", "off", "true", "3.5"):
pane.cmd("set-option", "-p", "@probe", raw)
got = pane.show_option("@probe")
print(f"{raw!r:8} -> {got!r:8} {type(got).__name__}")
'123' -> 123 int
'on' -> True bool
'off' -> False bool
'true' -> 'true' str
'3.5' -> '3.5' str
Why it bites
A @ option is the natural place to stamp an identity on a pane. When that identity is user-supplied, someone who names a thing 123, on or off gets back a value that no longer compares equal to what they set, so the pane cannot be found again.
Found while fixing awslabs/cli-agent-orchestrator, where it made a terminal named 123 unaddressable.
Note
The module docstring already treats these as their own category:
There are also custom user options, preceded with @, which exist are stored to Options.context.user_options as a dictionary.
Options.context.user_options does not appear anywhere else in the source, so that looks like an intent that was never implemented.
I have a patch that guards both call sites on the @ prefix and adds the round-trip case to test_custom_options. Built-in options keep converting (exit-unattached → False, history-limit → 2000). Happy to open it as a PR if you want it shaped that way — it changes behaviour for anyone relying on the current conversion, so it seemed like your call rather than mine.
Filed against tmux-python/libtmux v0.62.0, tmux 3.6a.
What happens
convert_valueapplies tmux's built-in option coercion to every value, and the call sites reach user options too:src/libtmux/options.py#L245—convert_values, dict branchsrc/libtmux/options.py#L1216—_show_option, direct lookuptmux(1)defines a user option as carrying an arbitrary string:So a user option does not survive the round trip when its value happens to read as a tmux boolean or a number:
show_optionreturns123123onTrueoffFalsetrue'true'3.5'3.5'show_options()returns the same converted values.The coercion is right for built-in options — tmux really does use
on/offthere. For a@name it is also not self-consistent:onconverts buttruedoes not,123converts but3.5does not.Repro
Why it bites
A
@option is the natural place to stamp an identity on a pane. When that identity is user-supplied, someone who names a thing123,onoroffgets back a value that no longer compares equal to what they set, so the pane cannot be found again.Found while fixing awslabs/cli-agent-orchestrator, where it made a terminal named
123unaddressable.Note
The module docstring already treats these as their own category:
Options.context.user_optionsdoes not appear anywhere else in the source, so that looks like an intent that was never implemented.I have a patch that guards both call sites on the
@prefix and adds the round-trip case totest_custom_options. Built-in options keep converting (exit-unattached→False,history-limit→2000). Happy to open it as a PR if you want it shaped that way — it changes behaviour for anyone relying on the current conversion, so it seemed like your call rather than mine.