comparison mercurial/utils/procutil.py @ 37459:90c5ca718781

procutil: rewrite popen() as a subprocess.Popen wrapper (issue4746) (API) os.popen() of Python 3 is not the popen() we want. First, it doesn't accept command in bytes. Second, a returned stream is always wrapped by TextIO. So we have to reimplement our popen(). Fortunately, this fixes the bug 4746 since ours returns an exit code compatible with explainexit(). .. api:: ``procutil.popen()`` no longer supports text mode I/O.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 07 Apr 2018 21:09:21 +0900
parents 7f78de1c93aa
children a6c6b7beb025
comparison
equal deleted inserted replaced
37458:00e4bd97b095 37459:90c5ca718781
56 findexe = platform.findexe 56 findexe = platform.findexe
57 _gethgcmd = platform.gethgcmd 57 _gethgcmd = platform.gethgcmd
58 getuser = platform.getuser 58 getuser = platform.getuser
59 getpid = os.getpid 59 getpid = os.getpid
60 hidewindow = platform.hidewindow 60 hidewindow = platform.hidewindow
61 popen = platform.popen
62 quotecommand = platform.quotecommand 61 quotecommand = platform.quotecommand
63 readpipe = platform.readpipe 62 readpipe = platform.readpipe
64 setbinary = platform.setbinary 63 setbinary = platform.setbinary
65 setsignalhandler = platform.setsignalhandler 64 setsignalhandler = platform.setsignalhandler
66 shellquote = platform.shellquote 65 shellquote = platform.shellquote
77 unblocksignal = osutil.unblocksignal 76 unblocksignal = osutil.unblocksignal
78 except AttributeError: 77 except AttributeError:
79 pass 78 pass
80 79
81 closefds = pycompat.isposix 80 closefds = pycompat.isposix
81
82 class _pfile(object):
83 """File-like wrapper for a stream opened by subprocess.Popen()"""
84
85 def __init__(self, proc, fp):
86 self._proc = proc
87 self._fp = fp
88
89 def close(self):
90 # unlike os.popen(), this returns an integer in subprocess coding
91 self._fp.close()
92 return self._proc.wait()
93
94 def __iter__(self):
95 return iter(self._fp)
96
97 def __getattr__(self, attr):
98 return getattr(self._fp, attr)
99
100 def __enter__(self):
101 return self
102
103 def __exit__(self, exc_type, exc_value, exc_tb):
104 self.close()
105
106 def popen(cmd, mode='rb', bufsize=-1):
107 if mode == 'rb':
108 return _popenreader(cmd, bufsize)
109 elif mode == 'wb':
110 return _popenwriter(cmd, bufsize)
111 raise error.ProgrammingError('unsupported mode: %r' % mode)
112
113 def _popenreader(cmd, bufsize):
114 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
115 close_fds=closefds,
116 stdout=subprocess.PIPE)
117 return _pfile(p, p.stdout)
118
119 def _popenwriter(cmd, bufsize):
120 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
121 close_fds=closefds,
122 stdin=subprocess.PIPE)
123 return _pfile(p, p.stdin)
82 124
83 def popen2(cmd, env=None, newlines=False): 125 def popen2(cmd, env=None, newlines=False):
84 # Setting bufsize to -1 lets the system decide the buffer size. 126 # Setting bufsize to -1 lets the system decide the buffer size.
85 # The default for bufsize is 0, meaning unbuffered. This leads to 127 # The default for bufsize is 0, meaning unbuffered. This leads to
86 # poor performance on Mac OS X: http://bugs.python.org/issue4194 128 # poor performance on Mac OS X: http://bugs.python.org/issue4194