--- /dev/null
+
+import sys, traceback
+from ctypes import pythonapi, cdll, c_size_t, c_char_p, c_void_p, cast, CFUNCTYPE, POINTER, addressof
+
+PyMem_Malloc = pythonapi.PyMem_Malloc
+PyMem_Malloc.restype = c_size_t
+PyMem_Malloc.argtypes = [c_size_t]
+
+strncpy = cdll.msvcrt.strncpy
+strncpy.restype = c_char_p
+strncpy.argtypes = [c_char_p, c_char_p, c_size_t]
+
+HOOKFUNC = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p)
+
+PyOS_ReadlineFunctionPointer = c_void_p.in_dll(pythonapi, "PyOS_ReadlineFunctionPointer")
+
+
+def new_zero_terminated_string(b):
+ p = PyMem_Malloc(len(b) + 1)
+ strncpy(cast(p, c_char_p), b, len(b) + 1)
+ return p
+
+
+class ReadlineHookManager:
+ def __init__(self):
+ self.readline_wrapper_ref = HOOKFUNC(self.readline_wrapper)
+ self.address = c_void_p.from_address(addressof(self.readline_wrapper_ref)).value
+ self.original_address = PyOS_ReadlineFunctionPointer.value
+ self.readline_hook = None
+
+ def readline_wrapper(self, stdin, stdout, prompt):
+ try:
+ try:
+ if sys.stdin.encoding != sys.stdout.encoding:
+ raise ValueError("sys.stdin.encoding != sys.stdout.encoding, readline hook doesn't know, which one to use to decode prompt")
+
+ except ValueError:
+ traceback.print_exc(file=sys.stderr)
+ try:
+ prompt = prompt.decode("utf-8")
+ except UnicodeDecodeError:
+ prompt = ""
+
+ else:
+ prompt = prompt.decode(sys.stdout.encoding)
+
+ try:
+ line = self.readline_hook(prompt)
+ except KeyboardInterrupt:
+ return 0
+ else:
+ return new_zero_terminated_string(line.encode(sys.stdin.encoding))
+
+ except:
+ print("Intenal win_unicode_console error", file=sys.stderr)
+ traceback.print_exc(file=sys.stderr)
+ return new_zero_terminated_string(b"\n")
+
+ def install_hook(self, hook):
+ self.readline_hook = hook
+ PyOS_ReadlineFunctionPointer.value = self.address
+
+ def restore_original(self):
+ self.readline_hook = None
+ PyOS_ReadlineFunctionPointer.value = self.original_address
+
+
+def readline(prompt):
+ sys.stdout.write(prompt)
+ sys.stdout.flush()
+ return sys.stdin.readline()
+
+
+class PyReadlineManager:
+ def __init__(self):
+ self.original_codepage = pyreadline.unicode_helper.pyreadline_codepage
+
+ def set_codepage(self, codepage):
+ pyreadline.unicode_helper.pyreadline_codepage = codepage
+
+ def restore_original(self):
+ self.set_codepage(self.original_codepage)
+
+try:
+ import pyreadline.unicode_helper
+except ImportError:
+ pyreadline = None
+else:
+ pyreadline_manager = PyReadlineManager()
+
+manager = ReadlineHookManager()
+
+
+def enable(*, use_pyreadline=True):
+ if use_pyreadline and pyreadline:
+ pyreadline_manager.set_codepage(sys.stdin.encoding)
+ # pyreadline assumes that encoding of all sys.stdio objects is the same
+ else:
+ manager.install_hook(readline)
+
+def disable():
+ if pyreadline:
+ pyreadline_manager.restore_original()
+
+ manager.restore_original()
+