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: