Mercurial > hg-stable
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 |