Mercurial > hg-stable
diff 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 |
line wrap: on
line diff
--- a/mercurial/utils/procutil.py Sat Oct 05 10:29:34 2019 -0400 +++ b/mercurial/utils/procutil.py Sun Oct 06 09:45:02 2019 -0400 @@ -34,12 +34,14 @@ stdin = pycompat.stdin stdout = pycompat.stdout + def isatty(fp): try: return fp.isatty() except AttributeError: return False + # glibc determines buffering on first write to stdout - if we replace a TTY # destined stdout with a pipe destined stdout (e.g. pager), we want line # buffering (or unbuffered, on Windows) @@ -52,6 +54,7 @@ if pycompat.iswindows: from .. import windows as platform + stdout = platform.winstdout(stdout) else: from .. import posix as platform @@ -83,6 +86,7 @@ closefds = pycompat.isposix + def explainexit(code): """return a message describing a subprocess status (codes from kill are negative - not os.system/wait encoding)""" @@ -90,6 +94,7 @@ return _("exited with status %d") % code return _("killed by signal %d") % -code + class _pfile(object): """File-like wrapper for a stream opened by subprocess.Popen()""" @@ -114,6 +119,7 @@ def __exit__(self, exc_type, exc_value, exc_tb): self.close() + def popen(cmd, mode='rb', bufsize=-1): if mode == 'rb': return _popenreader(cmd, bufsize) @@ -121,52 +127,77 @@ return _popenwriter(cmd, bufsize) raise error.ProgrammingError('unsupported mode: %r' % mode) + def _popenreader(cmd, bufsize): - p = subprocess.Popen(tonativestr(quotecommand(cmd)), - shell=True, bufsize=bufsize, - close_fds=closefds, - stdout=subprocess.PIPE) + p = subprocess.Popen( + tonativestr(quotecommand(cmd)), + shell=True, + bufsize=bufsize, + close_fds=closefds, + stdout=subprocess.PIPE, + ) return _pfile(p, p.stdout) + def _popenwriter(cmd, bufsize): - p = subprocess.Popen(tonativestr(quotecommand(cmd)), - shell=True, bufsize=bufsize, - close_fds=closefds, - stdin=subprocess.PIPE) + p = subprocess.Popen( + tonativestr(quotecommand(cmd)), + shell=True, + bufsize=bufsize, + close_fds=closefds, + stdin=subprocess.PIPE, + ) return _pfile(p, p.stdin) + def popen2(cmd, env=None): # Setting bufsize to -1 lets the system decide the buffer size. # The default for bufsize is 0, meaning unbuffered. This leads to # poor performance on Mac OS X: http://bugs.python.org/issue4194 - p = subprocess.Popen(tonativestr(cmd), - shell=True, bufsize=-1, - close_fds=closefds, - stdin=subprocess.PIPE, stdout=subprocess.PIPE, - env=tonativeenv(env)) + p = subprocess.Popen( + tonativestr(cmd), + shell=True, + bufsize=-1, + close_fds=closefds, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + env=tonativeenv(env), + ) return p.stdin, p.stdout + def popen3(cmd, env=None): stdin, stdout, stderr, p = popen4(cmd, env) return stdin, stdout, stderr + def popen4(cmd, env=None, bufsize=-1): - p = subprocess.Popen(tonativestr(cmd), - shell=True, bufsize=bufsize, - close_fds=closefds, - stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=tonativeenv(env)) + p = subprocess.Popen( + tonativestr(cmd), + shell=True, + bufsize=bufsize, + close_fds=closefds, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=tonativeenv(env), + ) return p.stdin, p.stdout, p.stderr, p + def pipefilter(s, cmd): '''filter string S through command CMD, returning its output''' - p = subprocess.Popen(tonativestr(cmd), - shell=True, close_fds=closefds, - stdin=subprocess.PIPE, stdout=subprocess.PIPE) + p = subprocess.Popen( + tonativestr(cmd), + shell=True, + close_fds=closefds, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) pout, perr = p.communicate(s) return pout + def tempfilter(s, cmd): '''filter string S through a pair of temporary files with CMD. CMD is used as a template to create the real command to be run, @@ -186,8 +217,9 @@ if pycompat.sysplatform == 'OpenVMS' and code & 1: code = 0 if code: - raise error.Abort(_("command '%s' failed: %s") % - (cmd, explainexit(code))) + raise error.Abort( + _("command '%s' failed: %s") % (cmd, explainexit(code)) + ) with open(outname, 'rb') as fp: return fp.read() finally: @@ -202,30 +234,37 @@ except OSError: pass + _filtertable = { 'tempfile:': tempfilter, 'pipe:': pipefilter, } + def filter(s, cmd): "filter a string through a command that transforms its input to its output" for name, fn in _filtertable.iteritems(): if cmd.startswith(name): - return fn(s, cmd[len(name):].lstrip()) + return fn(s, cmd[len(name) :].lstrip()) return pipefilter(s, cmd) + def mainfrozen(): """return True if we are a frozen executable. The code supports py2exe (most common, Windows only) and tools/freeze (portable, not much used). """ - return (pycompat.safehasattr(sys, "frozen") or # new py2exe - pycompat.safehasattr(sys, "importers") or # old py2exe - imp.is_frozen(r"__main__")) # tools/freeze + return ( + pycompat.safehasattr(sys, "frozen") + or pycompat.safehasattr(sys, "importers") # new py2exe + or imp.is_frozen(r"__main__") # old py2exe + ) # tools/freeze + _hgexecutable = None + def hgexecutable(): """return location of the 'hg' executable. @@ -242,32 +281,43 @@ _sethgexecutable(encoding.environ['EXECUTABLEPATH']) else: _sethgexecutable(pycompat.sysexecutable) - elif (not pycompat.iswindows and os.path.basename( - pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'): + elif ( + not pycompat.iswindows + and os.path.basename( + pycompat.fsencode(getattr(mainmod, '__file__', '')) + ) + == 'hg' + ): _sethgexecutable(pycompat.fsencode(mainmod.__file__)) else: - _sethgexecutable(findexe('hg') or - os.path.basename(pycompat.sysargv[0])) + _sethgexecutable( + findexe('hg') or os.path.basename(pycompat.sysargv[0]) + ) return _hgexecutable + def _sethgexecutable(path): """set location of the 'hg' executable""" global _hgexecutable _hgexecutable = path + def _testfileno(f, stdf): fileno = getattr(f, 'fileno', None) try: return fileno and fileno() == stdf.fileno() except io.UnsupportedOperation: - return False # fileno() raised UnsupportedOperation + return False # fileno() raised UnsupportedOperation + def isstdin(f): return _testfileno(f, sys.__stdin__) + def isstdout(f): return _testfileno(f, sys.__stdout__) + def protectstdio(uin, uout): """Duplicate streams and redirect original if (uin, uout) are stdio @@ -292,6 +342,7 @@ fout = os.fdopen(newfd, r'wb') return fin, fout + def restorestdio(uin, uout, fin, fout): """Restore (uin, uout) streams from possibly duplicated (fin, fout)""" uout.flush() @@ -300,8 +351,10 @@ os.dup2(f.fileno(), uif.fileno()) f.close() + def shellenviron(environ=None): """return environ with optional override, useful for shelling out""" + def py2shell(val): 'convert python object into string that is useful to shell' if val is None or val is False: @@ -309,28 +362,34 @@ if val is True: return '1' return pycompat.bytestr(val) + env = dict(encoding.environ) if environ: env.update((k, py2shell(v)) for k, v in environ.iteritems()) env['HG'] = hgexecutable() return env + if pycompat.iswindows: + def shelltonative(cmd, env): return platform.shelltocmdexe(cmd, shellenviron(env)) tonativestr = encoding.strfromlocal else: + def shelltonative(cmd, env): return cmd tonativestr = pycompat.identity + def tonativeenv(env): '''convert the environment from bytes to strings suitable for Popen(), etc. ''' return pycompat.rapply(tonativestr, env) + def system(cmd, environ=None, cwd=None, out=None): '''enhanced shell command execution. run with environment maybe modified, maybe in different dir. @@ -344,17 +403,23 @@ cmd = quotecommand(cmd) env = shellenviron(environ) if out is None or isstdout(out): - rc = subprocess.call(tonativestr(cmd), - shell=True, close_fds=closefds, - env=tonativeenv(env), - cwd=pycompat.rapply(tonativestr, cwd)) + rc = subprocess.call( + tonativestr(cmd), + shell=True, + close_fds=closefds, + env=tonativeenv(env), + cwd=pycompat.rapply(tonativestr, cwd), + ) else: - proc = subprocess.Popen(tonativestr(cmd), - shell=True, close_fds=closefds, - env=tonativeenv(env), - cwd=pycompat.rapply(tonativestr, cwd), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + proc = subprocess.Popen( + tonativestr(cmd), + shell=True, + close_fds=closefds, + env=tonativeenv(env), + cwd=pycompat.rapply(tonativestr, cwd), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) for line in iter(proc.stdout.readline, ''): out.write(line) proc.wait() @@ -363,6 +428,7 @@ rc = 0 return rc + def gui(): '''Are we running in a GUI?''' if pycompat.isdarwin: @@ -378,6 +444,7 @@ else: return pycompat.iswindows or encoding.environ.get("DISPLAY") + def hgcmd(): """Return the command used to execute current hg @@ -393,6 +460,7 @@ return [pycompat.sysexecutable] return _gethgcmd() + def rundetached(args, condfn): """Execute the argument list in a detached process. @@ -410,8 +478,10 @@ # running process on success. Instead we listen for SIGCHLD telling # us our child process terminated. terminated = set() + def handler(signum, frame): terminated.add(os.wait()) + prevhandler = None SIGCHLD = getattr(signal, 'SIGCHLD', None) if SIGCHLD is not None: @@ -419,8 +489,7 @@ try: pid = spawndetached(args) while not condfn(): - if ((pid in terminated or not testpid(pid)) - and not condfn()): + if (pid in terminated or not testpid(pid)) and not condfn(): return -1 time.sleep(0.1) return pid @@ -428,6 +497,7 @@ if prevhandler is not None: signal.signal(signal.SIGCHLD, prevhandler) + @contextlib.contextmanager def uninterruptible(warn): """Inhibit SIGINT handling on a region of code. @@ -461,6 +531,7 @@ if shouldbail: raise KeyboardInterrupt + if pycompat.iswindows: # no fork on Windows, but we can create a detached process # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx @@ -472,18 +543,27 @@ _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP def runbgcommand( - script, env, shell=False, stdout=None, stderr=None, ensurestart=True): + script, env, shell=False, stdout=None, stderr=None, ensurestart=True + ): '''Spawn a command without waiting for it to finish.''' # we can't use close_fds *and* redirect stdin. I'm not sure that we # need to because the detached process has no console connection. subprocess.Popen( tonativestr(script), - shell=shell, env=tonativeenv(env), close_fds=True, - creationflags=_creationflags, stdout=stdout, - stderr=stderr) + shell=shell, + env=tonativeenv(env), + close_fds=True, + creationflags=_creationflags, + stdout=stdout, + stderr=stderr, + ) + + else: + def runbgcommand( - cmd, env, shell=False, stdout=None, stderr=None, ensurestart=True): + cmd, env, shell=False, stdout=None, stderr=None, ensurestart=True + ): '''Spawn a command without waiting for it to finish.''' # double-fork to completely detach from the parent process # based on http://code.activestate.com/recipes/278731 @@ -496,7 +576,7 @@ if os.WIFEXITED(status): returncode = os.WEXITSTATUS(status) else: - returncode = -os.WTERMSIG(status) + returncode = -(os.WTERMSIG(status)) if returncode != 0: # The child process's return code is 0 on success, an errno # value on failure, or 255 if we don't have a valid errno @@ -507,8 +587,10 @@ # doesn't seem worth adding that complexity here, though.) if returncode == 255: returncode = errno.EINVAL - raise OSError(returncode, 'error running %r: %s' % - (cmd, os.strerror(returncode))) + raise OSError( + returncode, + 'error running %r: %s' % (cmd, os.strerror(returncode)), + ) return returncode = 255 @@ -525,11 +607,17 @@ # connect stdin to devnull to make sure the subprocess can't # muck up that stream for mercurial. subprocess.Popen( - cmd, shell=shell, env=env, close_fds=True, - stdin=stdin, stdout=stdout, stderr=stderr) + cmd, + shell=shell, + env=env, + close_fds=True, + stdin=stdin, + stdout=stdout, + stderr=stderr, + ) returncode = 0 except EnvironmentError as ex: - returncode = (ex.errno & 0xff) + returncode = ex.errno & 0xFF if returncode == 0: # This shouldn't happen, but just in case make sure the # return code is never 0 here.