Skip to content

Commit 04812d4

Browse files
Fix input of long unicode characters
- with two codepoints or more
1 parent 425f60b commit 04812d4

File tree

4 files changed

+93
-9
lines changed

4 files changed

+93
-9
lines changed

Lib/_pyrepl/base_eventqueue.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,19 @@ def push(self, char: int | bytes | str) -> None:
7373
"""
7474
Processes a character by updating the buffer and handling special key mappings.
7575
"""
76-
ord_char = char if isinstance(char, int) else ord(char)
77-
if ord_char > 255:
78-
assert isinstance(char, str)
79-
char = bytes(char.encode(self.encoding, "replace"))
76+
77+
if isinstance(char, bytes):
8078
self.buf.extend(char)
8179
else:
82-
char = bytes(bytearray((ord_char,)))
83-
self.buf.append(ord_char)
80+
81+
ord_char = char if isinstance(char, int) else ord(char)
82+
if ord_char > 255:
83+
assert isinstance(char, str)
84+
char = bytes(char.encode(self.encoding, "replace"))
85+
self.buf.extend(char)
86+
else:
87+
char = bytes(bytearray((ord_char,)))
88+
self.buf.append(ord_char)
8489

8590
if char in self.keymap:
8691
if self.keymap is self.compiled_keymap:
@@ -108,7 +113,9 @@ def push(self, char: int | bytes | str) -> None:
108113
try:
109114
decoded = bytes(self.buf).decode(self.encoding)
110115
except UnicodeError:
111-
return
116+
self.flush_buf()
117+
raise
112118
else:
113119
self.insert(Event('key', decoded, self.flush_buf()))
114-
self.keymap = self.compiled_keymap
120+
finally:
121+
self.keymap = self.compiled_keymap

Lib/_pyrepl/simple_interact.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,8 @@ def maybe_run_command(statement: str) -> bool:
158158
except MemoryError:
159159
console.write("\nMemoryError\n")
160160
console.resetbuffer()
161+
# except SystemExit:
162+
# break
163+
# except:
164+
# console.showtraceback()
165+
# console.resetbuffer()

Lib/_pyrepl/windows_console.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,8 @@ def get_event(self, block: bool = True) -> Event | None:
468468
return None
469469
elif self.__vt_support:
470470
# If virtual terminal is enabled, scanning VT sequences
471-
self.event_queue.push(rec.Event.KeyEvent.uChar.UnicodeChar)
471+
key_bytes = raw_key.encode(self.event_queue.encoding)
472+
self.event_queue.push(key_bytes)
472473
continue
473474

474475
if key_event.dwControlKeyState & ALT_ACTIVE:

Lib/test/test_pyrepl/test_eventqueue.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,77 @@ def test_push_unicode_character(self):
129129
self.assertEqual(eq.events[0].evt, "key")
130130
self.assertEqual(eq.events[0].data, "ч")
131131

132+
def test_push_unicode_character_as_bytes(self):
133+
eq = self.make_eventqueue()
134+
eq.keymap = {}
135+
136+
eq.push("ч".encode(eq.encoding, "replace"))
137+
e = eq.get()
138+
self.assertEqual(e.evt, "key")
139+
self.assertEqual(e.data, "ч")
140+
141+
def test_push_long_unicode_character_as_bytes(self):
142+
eq = self.make_eventqueue()
143+
eq.keymap = {}
144+
145+
def _event(evt, data, raw=None):
146+
r = raw if raw is not None else data.encode(eq.encoding)
147+
e = Event(evt, data, r)
148+
return e
149+
150+
def _push(keys):
151+
for k in keys:
152+
eq.push(k)
153+
154+
_push("\x1b[200")
155+
eq.push("ñ".encode(eq.encoding, "replace"))
156+
_push("\x1b[201")
157+
158+
self.assertEqual(eq.get(), _event("key", "\x1b"))
159+
self.assertEqual(eq.get(), _event("key", "["))
160+
self.assertEqual(eq.get(), _event("key", "2"))
161+
self.assertEqual(eq.get(), _event("key", "0"))
162+
self.assertEqual(eq.get(), _event("key", "0"))
163+
164+
self.assertEqual(eq.get(), _event("key", "ñ", bytearray(b'\xc3\xb1')))
165+
166+
self.assertEqual(eq.get(), _event("key", "\x1b"))
167+
self.assertEqual(eq.get(), _event("key", "["))
168+
self.assertEqual(eq.get(), _event("key", "2"))
169+
self.assertEqual(eq.get(), _event("key", "0"))
170+
self.assertEqual(eq.get(), _event("key", "1"))
171+
172+
def test_push_long_unicode_character(self):
173+
eq = self.make_eventqueue()
174+
eq.keymap = {}
175+
176+
def _event(evt, data, raw=None):
177+
r = raw if raw is not None else data.encode(eq.encoding)
178+
e = Event(evt, data, r)
179+
return e
180+
181+
def _push(keys):
182+
for k in keys:
183+
eq.push(k)
184+
185+
_push("\x1b[200")
186+
msg = "'utf-8' codec can't decode byte 0xf1 in position 0: unexpected end of data"
187+
with self.assertRaisesRegex(UnicodeDecodeError, msg):
188+
eq.push("ñ")
189+
_push("\x1b[201")
190+
191+
self.assertEqual(eq.get(), _event("key", "\x1b"))
192+
self.assertEqual(eq.get(), _event("key", "["))
193+
self.assertEqual(eq.get(), _event("key", "2"))
194+
self.assertEqual(eq.get(), _event("key", "0"))
195+
self.assertEqual(eq.get(), _event("key", "0"))
196+
197+
self.assertEqual(eq.get(), _event("key", "\x1b"))
198+
self.assertEqual(eq.get(), _event("key", "["))
199+
self.assertEqual(eq.get(), _event("key", "2"))
200+
self.assertEqual(eq.get(), _event("key", "0"))
201+
self.assertEqual(eq.get(), _event("key", "1"))
202+
132203

133204
@unittest.skipIf(support.MS_WINDOWS, "No Unix event queue on Windows")
134205
class TestUnixEventQueue(EventQueueTestBase, unittest.TestCase):

0 commit comments

Comments
 (0)