Mercurial > public > mercurial-scm > hg
comparison mercurial/utils/procutil.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 2cc453284d5c |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
32 | 32 |
33 stderr = pycompat.stderr | 33 stderr = pycompat.stderr |
34 stdin = pycompat.stdin | 34 stdin = pycompat.stdin |
35 stdout = pycompat.stdout | 35 stdout = pycompat.stdout |
36 | 36 |
37 | |
37 def isatty(fp): | 38 def isatty(fp): |
38 try: | 39 try: |
39 return fp.isatty() | 40 return fp.isatty() |
40 except AttributeError: | 41 except AttributeError: |
41 return False | 42 return False |
43 | |
42 | 44 |
43 # glibc determines buffering on first write to stdout - if we replace a TTY | 45 # glibc determines buffering on first write to stdout - if we replace a TTY |
44 # destined stdout with a pipe destined stdout (e.g. pager), we want line | 46 # destined stdout with a pipe destined stdout (e.g. pager), we want line |
45 # buffering (or unbuffered, on Windows) | 47 # buffering (or unbuffered, on Windows) |
46 if isatty(stdout): | 48 if isatty(stdout): |
50 else: | 52 else: |
51 stdout = os.fdopen(stdout.fileno(), r'wb', 1) | 53 stdout = os.fdopen(stdout.fileno(), r'wb', 1) |
52 | 54 |
53 if pycompat.iswindows: | 55 if pycompat.iswindows: |
54 from .. import windows as platform | 56 from .. import windows as platform |
57 | |
55 stdout = platform.winstdout(stdout) | 58 stdout = platform.winstdout(stdout) |
56 else: | 59 else: |
57 from .. import posix as platform | 60 from .. import posix as platform |
58 | 61 |
59 findexe = platform.findexe | 62 findexe = platform.findexe |
81 except AttributeError: | 84 except AttributeError: |
82 pass | 85 pass |
83 | 86 |
84 closefds = pycompat.isposix | 87 closefds = pycompat.isposix |
85 | 88 |
89 | |
86 def explainexit(code): | 90 def explainexit(code): |
87 """return a message describing a subprocess status | 91 """return a message describing a subprocess status |
88 (codes from kill are negative - not os.system/wait encoding)""" | 92 (codes from kill are negative - not os.system/wait encoding)""" |
89 if code >= 0: | 93 if code >= 0: |
90 return _("exited with status %d") % code | 94 return _("exited with status %d") % code |
91 return _("killed by signal %d") % -code | 95 return _("killed by signal %d") % -code |
92 | 96 |
97 | |
93 class _pfile(object): | 98 class _pfile(object): |
94 """File-like wrapper for a stream opened by subprocess.Popen()""" | 99 """File-like wrapper for a stream opened by subprocess.Popen()""" |
95 | 100 |
96 def __init__(self, proc, fp): | 101 def __init__(self, proc, fp): |
97 self._proc = proc | 102 self._proc = proc |
111 def __enter__(self): | 116 def __enter__(self): |
112 return self | 117 return self |
113 | 118 |
114 def __exit__(self, exc_type, exc_value, exc_tb): | 119 def __exit__(self, exc_type, exc_value, exc_tb): |
115 self.close() | 120 self.close() |
121 | |
116 | 122 |
117 def popen(cmd, mode='rb', bufsize=-1): | 123 def popen(cmd, mode='rb', bufsize=-1): |
118 if mode == 'rb': | 124 if mode == 'rb': |
119 return _popenreader(cmd, bufsize) | 125 return _popenreader(cmd, bufsize) |
120 elif mode == 'wb': | 126 elif mode == 'wb': |
121 return _popenwriter(cmd, bufsize) | 127 return _popenwriter(cmd, bufsize) |
122 raise error.ProgrammingError('unsupported mode: %r' % mode) | 128 raise error.ProgrammingError('unsupported mode: %r' % mode) |
123 | 129 |
130 | |
124 def _popenreader(cmd, bufsize): | 131 def _popenreader(cmd, bufsize): |
125 p = subprocess.Popen(tonativestr(quotecommand(cmd)), | 132 p = subprocess.Popen( |
126 shell=True, bufsize=bufsize, | 133 tonativestr(quotecommand(cmd)), |
127 close_fds=closefds, | 134 shell=True, |
128 stdout=subprocess.PIPE) | 135 bufsize=bufsize, |
136 close_fds=closefds, | |
137 stdout=subprocess.PIPE, | |
138 ) | |
129 return _pfile(p, p.stdout) | 139 return _pfile(p, p.stdout) |
130 | 140 |
141 | |
131 def _popenwriter(cmd, bufsize): | 142 def _popenwriter(cmd, bufsize): |
132 p = subprocess.Popen(tonativestr(quotecommand(cmd)), | 143 p = subprocess.Popen( |
133 shell=True, bufsize=bufsize, | 144 tonativestr(quotecommand(cmd)), |
134 close_fds=closefds, | 145 shell=True, |
135 stdin=subprocess.PIPE) | 146 bufsize=bufsize, |
147 close_fds=closefds, | |
148 stdin=subprocess.PIPE, | |
149 ) | |
136 return _pfile(p, p.stdin) | 150 return _pfile(p, p.stdin) |
151 | |
137 | 152 |
138 def popen2(cmd, env=None): | 153 def popen2(cmd, env=None): |
139 # Setting bufsize to -1 lets the system decide the buffer size. | 154 # Setting bufsize to -1 lets the system decide the buffer size. |
140 # The default for bufsize is 0, meaning unbuffered. This leads to | 155 # The default for bufsize is 0, meaning unbuffered. This leads to |
141 # poor performance on Mac OS X: http://bugs.python.org/issue4194 | 156 # poor performance on Mac OS X: http://bugs.python.org/issue4194 |
142 p = subprocess.Popen(tonativestr(cmd), | 157 p = subprocess.Popen( |
143 shell=True, bufsize=-1, | 158 tonativestr(cmd), |
144 close_fds=closefds, | 159 shell=True, |
145 stdin=subprocess.PIPE, stdout=subprocess.PIPE, | 160 bufsize=-1, |
146 env=tonativeenv(env)) | 161 close_fds=closefds, |
162 stdin=subprocess.PIPE, | |
163 stdout=subprocess.PIPE, | |
164 env=tonativeenv(env), | |
165 ) | |
147 return p.stdin, p.stdout | 166 return p.stdin, p.stdout |
167 | |
148 | 168 |
149 def popen3(cmd, env=None): | 169 def popen3(cmd, env=None): |
150 stdin, stdout, stderr, p = popen4(cmd, env) | 170 stdin, stdout, stderr, p = popen4(cmd, env) |
151 return stdin, stdout, stderr | 171 return stdin, stdout, stderr |
152 | 172 |
173 | |
153 def popen4(cmd, env=None, bufsize=-1): | 174 def popen4(cmd, env=None, bufsize=-1): |
154 p = subprocess.Popen(tonativestr(cmd), | 175 p = subprocess.Popen( |
155 shell=True, bufsize=bufsize, | 176 tonativestr(cmd), |
156 close_fds=closefds, | 177 shell=True, |
157 stdin=subprocess.PIPE, stdout=subprocess.PIPE, | 178 bufsize=bufsize, |
158 stderr=subprocess.PIPE, | 179 close_fds=closefds, |
159 env=tonativeenv(env)) | 180 stdin=subprocess.PIPE, |
181 stdout=subprocess.PIPE, | |
182 stderr=subprocess.PIPE, | |
183 env=tonativeenv(env), | |
184 ) | |
160 return p.stdin, p.stdout, p.stderr, p | 185 return p.stdin, p.stdout, p.stderr, p |
186 | |
161 | 187 |
162 def pipefilter(s, cmd): | 188 def pipefilter(s, cmd): |
163 '''filter string S through command CMD, returning its output''' | 189 '''filter string S through command CMD, returning its output''' |
164 p = subprocess.Popen(tonativestr(cmd), | 190 p = subprocess.Popen( |
165 shell=True, close_fds=closefds, | 191 tonativestr(cmd), |
166 stdin=subprocess.PIPE, stdout=subprocess.PIPE) | 192 shell=True, |
193 close_fds=closefds, | |
194 stdin=subprocess.PIPE, | |
195 stdout=subprocess.PIPE, | |
196 ) | |
167 pout, perr = p.communicate(s) | 197 pout, perr = p.communicate(s) |
168 return pout | 198 return pout |
199 | |
169 | 200 |
170 def tempfilter(s, cmd): | 201 def tempfilter(s, cmd): |
171 '''filter string S through a pair of temporary files with CMD. | 202 '''filter string S through a pair of temporary files with CMD. |
172 CMD is used as a template to create the real command to be run, | 203 CMD is used as a template to create the real command to be run, |
173 with the strings INFILE and OUTFILE replaced by the real names of | 204 with the strings INFILE and OUTFILE replaced by the real names of |
184 cmd = cmd.replace('OUTFILE', outname) | 215 cmd = cmd.replace('OUTFILE', outname) |
185 code = system(cmd) | 216 code = system(cmd) |
186 if pycompat.sysplatform == 'OpenVMS' and code & 1: | 217 if pycompat.sysplatform == 'OpenVMS' and code & 1: |
187 code = 0 | 218 code = 0 |
188 if code: | 219 if code: |
189 raise error.Abort(_("command '%s' failed: %s") % | 220 raise error.Abort( |
190 (cmd, explainexit(code))) | 221 _("command '%s' failed: %s") % (cmd, explainexit(code)) |
222 ) | |
191 with open(outname, 'rb') as fp: | 223 with open(outname, 'rb') as fp: |
192 return fp.read() | 224 return fp.read() |
193 finally: | 225 finally: |
194 try: | 226 try: |
195 if inname: | 227 if inname: |
200 if outname: | 232 if outname: |
201 os.unlink(outname) | 233 os.unlink(outname) |
202 except OSError: | 234 except OSError: |
203 pass | 235 pass |
204 | 236 |
237 | |
205 _filtertable = { | 238 _filtertable = { |
206 'tempfile:': tempfilter, | 239 'tempfile:': tempfilter, |
207 'pipe:': pipefilter, | 240 'pipe:': pipefilter, |
208 } | 241 } |
209 | 242 |
243 | |
210 def filter(s, cmd): | 244 def filter(s, cmd): |
211 "filter a string through a command that transforms its input to its output" | 245 "filter a string through a command that transforms its input to its output" |
212 for name, fn in _filtertable.iteritems(): | 246 for name, fn in _filtertable.iteritems(): |
213 if cmd.startswith(name): | 247 if cmd.startswith(name): |
214 return fn(s, cmd[len(name):].lstrip()) | 248 return fn(s, cmd[len(name) :].lstrip()) |
215 return pipefilter(s, cmd) | 249 return pipefilter(s, cmd) |
250 | |
216 | 251 |
217 def mainfrozen(): | 252 def mainfrozen(): |
218 """return True if we are a frozen executable. | 253 """return True if we are a frozen executable. |
219 | 254 |
220 The code supports py2exe (most common, Windows only) and tools/freeze | 255 The code supports py2exe (most common, Windows only) and tools/freeze |
221 (portable, not much used). | 256 (portable, not much used). |
222 """ | 257 """ |
223 return (pycompat.safehasattr(sys, "frozen") or # new py2exe | 258 return ( |
224 pycompat.safehasattr(sys, "importers") or # old py2exe | 259 pycompat.safehasattr(sys, "frozen") |
225 imp.is_frozen(r"__main__")) # tools/freeze | 260 or pycompat.safehasattr(sys, "importers") # new py2exe |
261 or imp.is_frozen(r"__main__") # old py2exe | |
262 ) # tools/freeze | |
263 | |
226 | 264 |
227 _hgexecutable = None | 265 _hgexecutable = None |
266 | |
228 | 267 |
229 def hgexecutable(): | 268 def hgexecutable(): |
230 """return location of the 'hg' executable. | 269 """return location of the 'hg' executable. |
231 | 270 |
232 Defaults to $HG or 'hg' in the search path. | 271 Defaults to $HG or 'hg' in the search path. |
240 if getattr(sys, 'frozen', None) == 'macosx_app': | 279 if getattr(sys, 'frozen', None) == 'macosx_app': |
241 # Env variable set by py2app | 280 # Env variable set by py2app |
242 _sethgexecutable(encoding.environ['EXECUTABLEPATH']) | 281 _sethgexecutable(encoding.environ['EXECUTABLEPATH']) |
243 else: | 282 else: |
244 _sethgexecutable(pycompat.sysexecutable) | 283 _sethgexecutable(pycompat.sysexecutable) |
245 elif (not pycompat.iswindows and os.path.basename( | 284 elif ( |
246 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'): | 285 not pycompat.iswindows |
286 and os.path.basename( | |
287 pycompat.fsencode(getattr(mainmod, '__file__', '')) | |
288 ) | |
289 == 'hg' | |
290 ): | |
247 _sethgexecutable(pycompat.fsencode(mainmod.__file__)) | 291 _sethgexecutable(pycompat.fsencode(mainmod.__file__)) |
248 else: | 292 else: |
249 _sethgexecutable(findexe('hg') or | 293 _sethgexecutable( |
250 os.path.basename(pycompat.sysargv[0])) | 294 findexe('hg') or os.path.basename(pycompat.sysargv[0]) |
295 ) | |
251 return _hgexecutable | 296 return _hgexecutable |
297 | |
252 | 298 |
253 def _sethgexecutable(path): | 299 def _sethgexecutable(path): |
254 """set location of the 'hg' executable""" | 300 """set location of the 'hg' executable""" |
255 global _hgexecutable | 301 global _hgexecutable |
256 _hgexecutable = path | 302 _hgexecutable = path |
303 | |
257 | 304 |
258 def _testfileno(f, stdf): | 305 def _testfileno(f, stdf): |
259 fileno = getattr(f, 'fileno', None) | 306 fileno = getattr(f, 'fileno', None) |
260 try: | 307 try: |
261 return fileno and fileno() == stdf.fileno() | 308 return fileno and fileno() == stdf.fileno() |
262 except io.UnsupportedOperation: | 309 except io.UnsupportedOperation: |
263 return False # fileno() raised UnsupportedOperation | 310 return False # fileno() raised UnsupportedOperation |
311 | |
264 | 312 |
265 def isstdin(f): | 313 def isstdin(f): |
266 return _testfileno(f, sys.__stdin__) | 314 return _testfileno(f, sys.__stdin__) |
267 | 315 |
316 | |
268 def isstdout(f): | 317 def isstdout(f): |
269 return _testfileno(f, sys.__stdout__) | 318 return _testfileno(f, sys.__stdout__) |
319 | |
270 | 320 |
271 def protectstdio(uin, uout): | 321 def protectstdio(uin, uout): |
272 """Duplicate streams and redirect original if (uin, uout) are stdio | 322 """Duplicate streams and redirect original if (uin, uout) are stdio |
273 | 323 |
274 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's | 324 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's |
290 newfd = os.dup(uout.fileno()) | 340 newfd = os.dup(uout.fileno()) |
291 os.dup2(stderr.fileno(), uout.fileno()) | 341 os.dup2(stderr.fileno(), uout.fileno()) |
292 fout = os.fdopen(newfd, r'wb') | 342 fout = os.fdopen(newfd, r'wb') |
293 return fin, fout | 343 return fin, fout |
294 | 344 |
345 | |
295 def restorestdio(uin, uout, fin, fout): | 346 def restorestdio(uin, uout, fin, fout): |
296 """Restore (uin, uout) streams from possibly duplicated (fin, fout)""" | 347 """Restore (uin, uout) streams from possibly duplicated (fin, fout)""" |
297 uout.flush() | 348 uout.flush() |
298 for f, uif in [(fin, uin), (fout, uout)]: | 349 for f, uif in [(fin, uin), (fout, uout)]: |
299 if f is not uif: | 350 if f is not uif: |
300 os.dup2(f.fileno(), uif.fileno()) | 351 os.dup2(f.fileno(), uif.fileno()) |
301 f.close() | 352 f.close() |
302 | 353 |
354 | |
303 def shellenviron(environ=None): | 355 def shellenviron(environ=None): |
304 """return environ with optional override, useful for shelling out""" | 356 """return environ with optional override, useful for shelling out""" |
357 | |
305 def py2shell(val): | 358 def py2shell(val): |
306 'convert python object into string that is useful to shell' | 359 'convert python object into string that is useful to shell' |
307 if val is None or val is False: | 360 if val is None or val is False: |
308 return '0' | 361 return '0' |
309 if val is True: | 362 if val is True: |
310 return '1' | 363 return '1' |
311 return pycompat.bytestr(val) | 364 return pycompat.bytestr(val) |
365 | |
312 env = dict(encoding.environ) | 366 env = dict(encoding.environ) |
313 if environ: | 367 if environ: |
314 env.update((k, py2shell(v)) for k, v in environ.iteritems()) | 368 env.update((k, py2shell(v)) for k, v in environ.iteritems()) |
315 env['HG'] = hgexecutable() | 369 env['HG'] = hgexecutable() |
316 return env | 370 return env |
317 | 371 |
372 | |
318 if pycompat.iswindows: | 373 if pycompat.iswindows: |
374 | |
319 def shelltonative(cmd, env): | 375 def shelltonative(cmd, env): |
320 return platform.shelltocmdexe(cmd, shellenviron(env)) | 376 return platform.shelltocmdexe(cmd, shellenviron(env)) |
321 | 377 |
322 tonativestr = encoding.strfromlocal | 378 tonativestr = encoding.strfromlocal |
323 else: | 379 else: |
380 | |
324 def shelltonative(cmd, env): | 381 def shelltonative(cmd, env): |
325 return cmd | 382 return cmd |
326 | 383 |
327 tonativestr = pycompat.identity | 384 tonativestr = pycompat.identity |
385 | |
328 | 386 |
329 def tonativeenv(env): | 387 def tonativeenv(env): |
330 '''convert the environment from bytes to strings suitable for Popen(), etc. | 388 '''convert the environment from bytes to strings suitable for Popen(), etc. |
331 ''' | 389 ''' |
332 return pycompat.rapply(tonativestr, env) | 390 return pycompat.rapply(tonativestr, env) |
391 | |
333 | 392 |
334 def system(cmd, environ=None, cwd=None, out=None): | 393 def system(cmd, environ=None, cwd=None, out=None): |
335 '''enhanced shell command execution. | 394 '''enhanced shell command execution. |
336 run with environment maybe modified, maybe in different dir. | 395 run with environment maybe modified, maybe in different dir. |
337 | 396 |
342 except Exception: | 401 except Exception: |
343 pass | 402 pass |
344 cmd = quotecommand(cmd) | 403 cmd = quotecommand(cmd) |
345 env = shellenviron(environ) | 404 env = shellenviron(environ) |
346 if out is None or isstdout(out): | 405 if out is None or isstdout(out): |
347 rc = subprocess.call(tonativestr(cmd), | 406 rc = subprocess.call( |
348 shell=True, close_fds=closefds, | 407 tonativestr(cmd), |
349 env=tonativeenv(env), | 408 shell=True, |
350 cwd=pycompat.rapply(tonativestr, cwd)) | 409 close_fds=closefds, |
410 env=tonativeenv(env), | |
411 cwd=pycompat.rapply(tonativestr, cwd), | |
412 ) | |
351 else: | 413 else: |
352 proc = subprocess.Popen(tonativestr(cmd), | 414 proc = subprocess.Popen( |
353 shell=True, close_fds=closefds, | 415 tonativestr(cmd), |
354 env=tonativeenv(env), | 416 shell=True, |
355 cwd=pycompat.rapply(tonativestr, cwd), | 417 close_fds=closefds, |
356 stdout=subprocess.PIPE, | 418 env=tonativeenv(env), |
357 stderr=subprocess.STDOUT) | 419 cwd=pycompat.rapply(tonativestr, cwd), |
420 stdout=subprocess.PIPE, | |
421 stderr=subprocess.STDOUT, | |
422 ) | |
358 for line in iter(proc.stdout.readline, ''): | 423 for line in iter(proc.stdout.readline, ''): |
359 out.write(line) | 424 out.write(line) |
360 proc.wait() | 425 proc.wait() |
361 rc = proc.returncode | 426 rc = proc.returncode |
362 if pycompat.sysplatform == 'OpenVMS' and rc & 1: | 427 if pycompat.sysplatform == 'OpenVMS' and rc & 1: |
363 rc = 0 | 428 rc = 0 |
364 return rc | 429 return rc |
430 | |
365 | 431 |
366 def gui(): | 432 def gui(): |
367 '''Are we running in a GUI?''' | 433 '''Are we running in a GUI?''' |
368 if pycompat.isdarwin: | 434 if pycompat.isdarwin: |
369 if 'SSH_CONNECTION' in encoding.environ: | 435 if 'SSH_CONNECTION' in encoding.environ: |
376 # pure build; use a safe default | 442 # pure build; use a safe default |
377 return True | 443 return True |
378 else: | 444 else: |
379 return pycompat.iswindows or encoding.environ.get("DISPLAY") | 445 return pycompat.iswindows or encoding.environ.get("DISPLAY") |
380 | 446 |
447 | |
381 def hgcmd(): | 448 def hgcmd(): |
382 """Return the command used to execute current hg | 449 """Return the command used to execute current hg |
383 | 450 |
384 This is different from hgexecutable() because on Windows we want | 451 This is different from hgexecutable() because on Windows we want |
385 to avoid things opening new shell windows like batch files, so we | 452 to avoid things opening new shell windows like batch files, so we |
390 # Env variable set by py2app | 457 # Env variable set by py2app |
391 return [encoding.environ['EXECUTABLEPATH']] | 458 return [encoding.environ['EXECUTABLEPATH']] |
392 else: | 459 else: |
393 return [pycompat.sysexecutable] | 460 return [pycompat.sysexecutable] |
394 return _gethgcmd() | 461 return _gethgcmd() |
462 | |
395 | 463 |
396 def rundetached(args, condfn): | 464 def rundetached(args, condfn): |
397 """Execute the argument list in a detached process. | 465 """Execute the argument list in a detached process. |
398 | 466 |
399 condfn is a callable which is called repeatedly and should return | 467 condfn is a callable which is called repeatedly and should return |
408 # process fails to start, it will be left in a zombie state until | 476 # process fails to start, it will be left in a zombie state until |
409 # the parent wait on it, which we cannot do since we expect a long | 477 # the parent wait on it, which we cannot do since we expect a long |
410 # running process on success. Instead we listen for SIGCHLD telling | 478 # running process on success. Instead we listen for SIGCHLD telling |
411 # us our child process terminated. | 479 # us our child process terminated. |
412 terminated = set() | 480 terminated = set() |
481 | |
413 def handler(signum, frame): | 482 def handler(signum, frame): |
414 terminated.add(os.wait()) | 483 terminated.add(os.wait()) |
484 | |
415 prevhandler = None | 485 prevhandler = None |
416 SIGCHLD = getattr(signal, 'SIGCHLD', None) | 486 SIGCHLD = getattr(signal, 'SIGCHLD', None) |
417 if SIGCHLD is not None: | 487 if SIGCHLD is not None: |
418 prevhandler = signal.signal(SIGCHLD, handler) | 488 prevhandler = signal.signal(SIGCHLD, handler) |
419 try: | 489 try: |
420 pid = spawndetached(args) | 490 pid = spawndetached(args) |
421 while not condfn(): | 491 while not condfn(): |
422 if ((pid in terminated or not testpid(pid)) | 492 if (pid in terminated or not testpid(pid)) and not condfn(): |
423 and not condfn()): | |
424 return -1 | 493 return -1 |
425 time.sleep(0.1) | 494 time.sleep(0.1) |
426 return pid | 495 return pid |
427 finally: | 496 finally: |
428 if prevhandler is not None: | 497 if prevhandler is not None: |
429 signal.signal(signal.SIGCHLD, prevhandler) | 498 signal.signal(signal.SIGCHLD, prevhandler) |
499 | |
430 | 500 |
431 @contextlib.contextmanager | 501 @contextlib.contextmanager |
432 def uninterruptible(warn): | 502 def uninterruptible(warn): |
433 """Inhibit SIGINT handling on a region of code. | 503 """Inhibit SIGINT handling on a region of code. |
434 | 504 |
459 if oldsiginthandler: | 529 if oldsiginthandler: |
460 signal.signal(signal.SIGINT, oldsiginthandler[0]) | 530 signal.signal(signal.SIGINT, oldsiginthandler[0]) |
461 if shouldbail: | 531 if shouldbail: |
462 raise KeyboardInterrupt | 532 raise KeyboardInterrupt |
463 | 533 |
534 | |
464 if pycompat.iswindows: | 535 if pycompat.iswindows: |
465 # no fork on Windows, but we can create a detached process | 536 # no fork on Windows, but we can create a detached process |
466 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx | 537 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx |
467 # No stdlib constant exists for this value | 538 # No stdlib constant exists for this value |
468 DETACHED_PROCESS = 0x00000008 | 539 DETACHED_PROCESS = 0x00000008 |
470 # Using subprocess.CREATE_NEW_CONSOLE might helps. | 541 # Using subprocess.CREATE_NEW_CONSOLE might helps. |
471 # See https://phab.mercurial-scm.org/D1701 for discussion | 542 # See https://phab.mercurial-scm.org/D1701 for discussion |
472 _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP | 543 _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP |
473 | 544 |
474 def runbgcommand( | 545 def runbgcommand( |
475 script, env, shell=False, stdout=None, stderr=None, ensurestart=True): | 546 script, env, shell=False, stdout=None, stderr=None, ensurestart=True |
547 ): | |
476 '''Spawn a command without waiting for it to finish.''' | 548 '''Spawn a command without waiting for it to finish.''' |
477 # we can't use close_fds *and* redirect stdin. I'm not sure that we | 549 # we can't use close_fds *and* redirect stdin. I'm not sure that we |
478 # need to because the detached process has no console connection. | 550 # need to because the detached process has no console connection. |
479 subprocess.Popen( | 551 subprocess.Popen( |
480 tonativestr(script), | 552 tonativestr(script), |
481 shell=shell, env=tonativeenv(env), close_fds=True, | 553 shell=shell, |
482 creationflags=_creationflags, stdout=stdout, | 554 env=tonativeenv(env), |
483 stderr=stderr) | 555 close_fds=True, |
556 creationflags=_creationflags, | |
557 stdout=stdout, | |
558 stderr=stderr, | |
559 ) | |
560 | |
561 | |
484 else: | 562 else: |
563 | |
485 def runbgcommand( | 564 def runbgcommand( |
486 cmd, env, shell=False, stdout=None, stderr=None, ensurestart=True): | 565 cmd, env, shell=False, stdout=None, stderr=None, ensurestart=True |
566 ): | |
487 '''Spawn a command without waiting for it to finish.''' | 567 '''Spawn a command without waiting for it to finish.''' |
488 # double-fork to completely detach from the parent process | 568 # double-fork to completely detach from the parent process |
489 # based on http://code.activestate.com/recipes/278731 | 569 # based on http://code.activestate.com/recipes/278731 |
490 pid = os.fork() | 570 pid = os.fork() |
491 if pid: | 571 if pid: |
494 # Parent process | 574 # Parent process |
495 (_pid, status) = os.waitpid(pid, 0) | 575 (_pid, status) = os.waitpid(pid, 0) |
496 if os.WIFEXITED(status): | 576 if os.WIFEXITED(status): |
497 returncode = os.WEXITSTATUS(status) | 577 returncode = os.WEXITSTATUS(status) |
498 else: | 578 else: |
499 returncode = -os.WTERMSIG(status) | 579 returncode = -(os.WTERMSIG(status)) |
500 if returncode != 0: | 580 if returncode != 0: |
501 # The child process's return code is 0 on success, an errno | 581 # The child process's return code is 0 on success, an errno |
502 # value on failure, or 255 if we don't have a valid errno | 582 # value on failure, or 255 if we don't have a valid errno |
503 # value. | 583 # value. |
504 # | 584 # |
505 # (It would be slightly nicer to return the full exception info | 585 # (It would be slightly nicer to return the full exception info |
506 # over a pipe as the subprocess module does. For now it | 586 # over a pipe as the subprocess module does. For now it |
507 # doesn't seem worth adding that complexity here, though.) | 587 # doesn't seem worth adding that complexity here, though.) |
508 if returncode == 255: | 588 if returncode == 255: |
509 returncode = errno.EINVAL | 589 returncode = errno.EINVAL |
510 raise OSError(returncode, 'error running %r: %s' % | 590 raise OSError( |
511 (cmd, os.strerror(returncode))) | 591 returncode, |
592 'error running %r: %s' % (cmd, os.strerror(returncode)), | |
593 ) | |
512 return | 594 return |
513 | 595 |
514 returncode = 255 | 596 returncode = 255 |
515 try: | 597 try: |
516 # Start a new session | 598 # Start a new session |
523 stderr = open(os.devnull, 'w') | 605 stderr = open(os.devnull, 'w') |
524 | 606 |
525 # connect stdin to devnull to make sure the subprocess can't | 607 # connect stdin to devnull to make sure the subprocess can't |
526 # muck up that stream for mercurial. | 608 # muck up that stream for mercurial. |
527 subprocess.Popen( | 609 subprocess.Popen( |
528 cmd, shell=shell, env=env, close_fds=True, | 610 cmd, |
529 stdin=stdin, stdout=stdout, stderr=stderr) | 611 shell=shell, |
612 env=env, | |
613 close_fds=True, | |
614 stdin=stdin, | |
615 stdout=stdout, | |
616 stderr=stderr, | |
617 ) | |
530 returncode = 0 | 618 returncode = 0 |
531 except EnvironmentError as ex: | 619 except EnvironmentError as ex: |
532 returncode = (ex.errno & 0xff) | 620 returncode = ex.errno & 0xFF |
533 if returncode == 0: | 621 if returncode == 0: |
534 # This shouldn't happen, but just in case make sure the | 622 # This shouldn't happen, but just in case make sure the |
535 # return code is never 0 here. | 623 # return code is never 0 here. |
536 returncode = 255 | 624 returncode = 255 |
537 except Exception: | 625 except Exception: |