prompt-toolkit / prompt-toolkit/python-prompt-toolkit
Character-valued `ANSI_SEQUENCES` entries insert their escape sequence instead of the character
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 10.6k
- Forks
- 815
- PR merge metrics
- No merged PRs in 30d
Description
ANSI_SEQUENCES maps an escape sequence to a key, and a key may be a plain single character rather than a Keys member. Such an entry resolves correctly but types its own escape sequence into the buffer.
Repro (3.0.53, no other dependencies):
from prompt_toolkit.input.ansi_escape_sequences import ANSI_SEQUENCES
from prompt_toolkit.input.vt100_parser import Vt100Parser
SEQ = "\x1b[27;2;78~" # what a terminal in xterm modifyOtherKeys=2 sends for Shift+N
ANSI_SEQUENCES[SEQ] = "N"
presses = []
p = Vt100Parser(presses.append)
p.feed(SEQ); p.flush()
print(presses[0].key, repr(presses[0].data))
# -> N '\x1b[27;2;78~'
End to end, through a real PromptSession:
import asyncio
from prompt_toolkit.input.ansi_escape_sequences import ANSI_SEQUENCES
from prompt_toolkit.input import create_pipe_input
from prompt_toolkit.output import DummyOutput
from prompt_toolkit.shortcuts import PromptSession
SEQ = "\x1b[27;2;78~"
ANSI_SEQUENCES[SEQ] = "N"
async def main():
with create_pipe_input() as pipe:
pipe.send_text("ab" + SEQ + "cd\r")
return await PromptSession(input=pipe, output=DummyOutput()).prompt_async()
print(repr(asyncio.run(main())))
# expected: 'abNcd'
# actual: 'ab\x1b[27;2;78~cd'
Mechanism
Vt100Parser._call_handler(key, insert_text)is called withinsert_text= the matched bytes, and buildsKeyPress(key, insert_text).key_binding/bindings/basic.py:168bindsKeys.Any→self-insert.key_binding/bindings/named_commands.py:273—self-insertdoesinsert_text(event.data * event.arg).
So insertion is driven by data, and data is the raw sequence. This is invisible for every entry prompt_toolkit ships, because those resolve to Keys members whose bindings never read data. It only bites character-valued entries, which the table accepts without complaint.
Why anyone registers those
There's no built-in support for xterm modifyOtherKeys / the kitty keyboard protocol (#2006 has been open for this since 2025-08-19). The natural workaround is to push the terminal into CSI >4;2m and register the resulting sequences as character-valued aliases — at which point every shifted letter types its own escape sequence.
Downstream this is currently handled by monkeypatching _call_handler. Hermes Agent has four independent open implementations of that same patch — NousResearch/hermes-agent#87785, #88097, #94385, #96133 — plus roughly ten more PRs and six issues against variants of the symptom (Shift+letter, Shift+Space, Shift+symbol, numeric keypad, Cyrillic, Cmd/Super). That seemed like enough duplicated effort to be worth reporting at the source rather than patching around again.
Ask: either narrow data to the key for character-valued entries, or document that ANSI_SEQUENCES values must be Keys members. Silently accepting a character and then typing the bytes is the part that costs people time — either resolution fixes that.
Happy to send a PR for whichever shape you'd prefer. Deliberately not opening one first: data is read at ~46 sites in the library, including mouse handling that slices it positionally, so which way this should be resolved seems like your call rather than something to presume in a diff.
Versions: reproduced on 3.0.52 and 3.0.53, Python 3.11.16, Linux.
Repros written and run by Nyx, a Claude Code agent, on the reporting machine; both are copy-pasteable and were executed as shown.
Contributor guide
No contributing guide indexed for this repository
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 with Vt100Parser._call_handler, then inspect key_binding/bindings/basic.py and key_binding/bindings/named_commands.py, where the issue traces how key and data reach self-insert. Run the supplied parser and PromptSession repros, and review the other data consumers mentioned in the report. Done means character-valued ANSI_SEQUENCES entries insert the character while existing sequence handling remains correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100