Patchwork [6,of,8] ui: do not use rawinput() when we have to replace sys.stdin/stdout

login
register
mail settings
Submitter Yuya Nishihara
Date March 9, 2018, 12:35 p.m.
Message ID <ad7ff97565b261d82952.1520598939@mimosa>
Download mbox | patch
Permalink /patch/29166/
State Accepted
Headers show

Comments

Yuya Nishihara - March 9, 2018, 12:35 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1520325533 21600
#      Tue Mar 06 02:38:53 2018 -0600
# Node ID ad7ff97565b261d82952acc9f941e5dd99f11374
# Parent  63a13b91e1ab4d9fa0a713935be58794b9cadab5
ui: do not use rawinput() when we have to replace sys.stdin/stdout

See the inline comment for why. The current Python3 hack doesn't work if
more than one user inputs are expected because TextIOWrapper fills its
internal buffer at the first read() request.

Maybe we could write an unbuffered TextIOWrapper, but I don't want to make
things more complicated. Instead, this patch reinvents raw_input(' ') of
no readline support.

Patch

diff --git a/contrib/python3-whitelist b/contrib/python3-whitelist
--- a/contrib/python3-whitelist
+++ b/contrib/python3-whitelist
@@ -3,6 +3,7 @@  test-add.t
 test-addremove-similar.t
 test-addremove.t
 test-amend-subrepo.t
+test-amend.t
 test-ancestor.py
 test-annotate.py
 test-annotate.t
@@ -185,6 +186,8 @@  test-journal-exists.t
 test-largefiles-cache.t
 test-largefiles-misc.t
 test-largefiles-small-disk.t
+test-largefiles-update.t
+test-lfs-largefiles.t
 test-locate.t
 test-lock-badness.t
 test-log.t
@@ -196,11 +199,13 @@  test-manifest.py
 test-manifest.t
 test-match.py
 test-mdiff.py
+test-merge-changedelete.t
 test-merge-closedheads.t
 test-merge-commit.t
 test-merge-criss-cross.t
 test-merge-default.t
 test-merge-force.t
+test-merge-halt.t
 test-merge-internal-tools-pattern.t
 test-merge-local.t
 test-merge-remove.t
@@ -228,11 +233,13 @@  test-mq-qgoto.t
 test-mq-qimport-fail-cleanup.t
 test-mq-qpush-exact.t
 test-mq-qqueue.t
+test-mq-qrefresh-interactive.t
 test-mq-qrefresh-replace-log-message.t
 test-mq-qrefresh.t
 test-mq-qrename.t
 test-mq-qsave.t
 test-mq-safety.t
+test-mq-subrepo.t
 test-mq-symlinks.t
 test-mv-cp-st-diff.t
 test-narrow-archive.t
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -1264,6 +1264,10 @@  class ui(object):
         return i
 
     def _readline(self):
+        # Replacing stdin/stdout temporarily is a hard problem on Python 3
+        # because they have to be text streams with *no buffering*. Instead,
+        # we use rawinput() only if call_readline() will be invoked by
+        # PyOS_Readline(), so no I/O will be made at Python layer.
         usereadline = (self._isatty(self.fin) and self._isatty(self.fout)
                        and util.isstdin(self.fin) and util.isstdout(self.fout))
         if usereadline:
@@ -1280,13 +1284,16 @@  class ui(object):
         # prompt ' ' must exist; otherwise readline may delete entire line
         # - http://bugs.python.org/issue12833
         with self.timeblockedsection('stdio'):
-            sin, sout = sys.stdin, sys.stdout
-            try:
-                sys.stdin = encoding.strio(self.fin)
-                sys.stdout = encoding.strio(self.fout)
+            if usereadline:
                 line = encoding.strtolocal(pycompat.rawinput(r' '))
-            finally:
-                sys.stdin, sys.stdout = sin, sout
+            else:
+                self.fout.write(b' ')
+                self.fout.flush()
+                line = self.fin.readline()
+                if not line:
+                    raise EOFError
+                if line.endswith(pycompat.oslinesep):
+                    line = line[:-len(pycompat.oslinesep)]
 
         # When stdin is in binary mode on Windows, it can cause
         # raw_input() to emit an extra trailing carriage return