tmux-python / tmux-python/libtmux
`show_option` coerces user options, so an `@` option set to `123` or `on` comes back as an int or a bool
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.2k
- Forks
- 127
- Avg merge
- 2h 13m
- Merged PRs (30d)
- 1
Description
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_optionsas 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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/libtmux/options.py at convert_values around line 245 and _show_option around line 1216, then read the existing test_custom_options coverage. Verify the behavior for @ options using the reported values, while confirming built-in options such as exit-unattached and history-limit still undergo conversion. Done means user-option strings round-trip unchanged and the relevant tests pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100