Merge branch 'cassandra-6.0' into trunk

* cassandra-6.0:
  run_cqlsh.py: fix parsing of a multi-byte UTF-8 sequence, os.read()/pipe reads can split the sequence across two reads
This commit is contained in:
Dmitry Konstantinov 2026-06-23 13:01:41 +01:00
commit aaae5b6417
1 changed files with 25 additions and 8 deletions

View File

@ -19,6 +19,7 @@
import os import os
import sys import sys
import re import re
import codecs
import contextlib import contextlib
import subprocess import subprocess
import signal import signal
@ -116,6 +117,12 @@ class ProcRunner:
env = {} env = {}
self.env = env self.env = env
self.readbuf = '' self.readbuf = ''
# os.read()/pipe reads can split a multi-byte UTF-8 sequence across two
# reads (the boundary depends on terminal/readline echo flushing, which
# changed with the Ubuntu 22.04 / readline 8.1 build image). Decode
# incrementally so a partial trailing sequence is buffered until the
# rest of its bytes arrive, instead of failing with UnicodeDecodeError.
self._decoder = codecs.getincrementaldecoder("utf-8")()
self.start_proc() self.start_proc()
@ -160,16 +167,26 @@ class ProcRunner:
self.proc.stdin.write(data) self.proc.stdin.write(data)
def read_tty(self, blksize, timeout=None): def read_tty(self, blksize, timeout=None):
buf = os.read(self.childpty, blksize) while True:
if isinstance(buf, bytes): buf = os.read(self.childpty, blksize)
buf = buf.decode("utf-8") if not isinstance(buf, bytes):
return buf return buf
decoded = self._decoder.decode(buf)
# A non-empty read that decodes to '' means we only got the start of
# a multi-byte UTF-8 sequence; keep reading until it completes rather
# than returning '', which callers (read_until) treat as EOF.
# An empty read (b'') is genuine EOF and must propagate as ''.
if decoded != '' or buf == b'':
return decoded
def read_pipe(self, blksize, timeout=None): def read_pipe(self, blksize, timeout=None):
buf = self.proc.stdout.read(blksize) while True:
if isinstance(buf, bytes): buf = self.proc.stdout.read(blksize)
buf = buf.decode("utf-8") if not isinstance(buf, bytes):
return buf return buf
decoded = self._decoder.decode(buf)
if decoded != '' or buf == b'':
return decoded
def read_until(self, until, blksize=4096, timeout=None, def read_until(self, until, blksize=4096, timeout=None,
flags=0, ptty_timeout=None, replace=None): flags=0, ptty_timeout=None, replace=None):