diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-02-10 16:44:30 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:30 +0300 |
commit | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (patch) | |
tree | 012bb94d777798f1f56ac1cec429509766d05181 /contrib/python/ipython/py3/IPython/utils/_process_posix.py | |
parent | 6751af0b0c1b952fede40b19b71da8025b5d8bcf (diff) | |
download | ydb-2598ef1d0aee359b4b6d5fdd1758916d5907d04f.tar.gz |
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/ipython/py3/IPython/utils/_process_posix.py')
-rw-r--r-- | contrib/python/ipython/py3/IPython/utils/_process_posix.py | 450 |
1 files changed, 225 insertions, 225 deletions
diff --git a/contrib/python/ipython/py3/IPython/utils/_process_posix.py b/contrib/python/ipython/py3/IPython/utils/_process_posix.py index a11cad7697..8a4bf737c3 100644 --- a/contrib/python/ipython/py3/IPython/utils/_process_posix.py +++ b/contrib/python/ipython/py3/IPython/utils/_process_posix.py @@ -1,225 +1,225 @@ -"""Posix-specific implementation of process utilities. - -This file is only meant to be imported by process.py, not by end-users. -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2010-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -# Stdlib -import errno -import os -import subprocess as sp -import sys - -import pexpect - -# Our own -from ._process_common import getoutput, arg_split -from IPython.utils import py3compat -from IPython.utils.encoding import DEFAULT_ENCODING - -#----------------------------------------------------------------------------- -# Function definitions -#----------------------------------------------------------------------------- - -def _find_cmd(cmd): - """Find the full path to a command using which.""" - - path = sp.Popen(['/usr/bin/env', 'which', cmd], - stdout=sp.PIPE, stderr=sp.PIPE).communicate()[0] - return py3compat.decode(path) - - -class ProcessHandler(object): - """Execute subprocesses under the control of pexpect. - """ - # Timeout in seconds to wait on each reading of the subprocess' output. - # This should not be set too low to avoid cpu overusage from our side, - # since we read in a loop whose period is controlled by this timeout. - read_timeout = 0.05 - - # Timeout to give a process if we receive SIGINT, between sending the - # SIGINT to the process and forcefully terminating it. - terminate_timeout = 0.2 - - # File object where stdout and stderr of the subprocess will be written - logfile = None - - # Shell to call for subprocesses to execute - _sh = None - - @property - def sh(self): - if self._sh is None: - shell_name = os.environ.get("SHELL", "sh") - self._sh = pexpect.which(shell_name) - if self._sh is None: - raise OSError('"{}" shell not found'.format(shell_name)) - - return self._sh - - def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None): - """Arguments are used for pexpect calls.""" - self.read_timeout = (ProcessHandler.read_timeout if read_timeout is - None else read_timeout) - self.terminate_timeout = (ProcessHandler.terminate_timeout if - terminate_timeout is None else - terminate_timeout) - self.logfile = sys.stdout if logfile is None else logfile - - def getoutput(self, cmd): - """Run a command and return its stdout/stderr as a string. - - Parameters - ---------- - cmd : str - A command to be executed in the system shell. - - Returns - ------- - output : str - A string containing the combination of stdout and stderr from the - subprocess, in whatever order the subprocess originally wrote to its - file descriptors (so the order of the information in this string is the - correct order as would be seen if running the command in a terminal). - """ - try: - return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n') - except KeyboardInterrupt: - print('^C', file=sys.stderr, end='') - - def getoutput_pexpect(self, cmd): - """Run a command and return its stdout/stderr as a string. - - Parameters - ---------- - cmd : str - A command to be executed in the system shell. - - Returns - ------- - output : str - A string containing the combination of stdout and stderr from the - subprocess, in whatever order the subprocess originally wrote to its - file descriptors (so the order of the information in this string is the - correct order as would be seen if running the command in a terminal). - """ - try: - return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n') - except KeyboardInterrupt: - print('^C', file=sys.stderr, end='') - - def system(self, cmd): - """Execute a command in a subshell. - - Parameters - ---------- - cmd : str - A command to be executed in the system shell. - - Returns - ------- - int : child's exitstatus - """ - # Get likely encoding for the output. - enc = DEFAULT_ENCODING - - # Patterns to match on the output, for pexpect. We read input and - # allow either a short timeout or EOF - patterns = [pexpect.TIMEOUT, pexpect.EOF] - # the index of the EOF pattern in the list. - # even though we know it's 1, this call means we don't have to worry if - # we change the above list, and forget to change this value: - EOF_index = patterns.index(pexpect.EOF) - # The size of the output stored so far in the process output buffer. - # Since pexpect only appends to this buffer, each time we print we - # record how far we've printed, so that next time we only print *new* - # content from the buffer. - out_size = 0 - try: - # Since we're not really searching the buffer for text patterns, we - # can set pexpect's search window to be tiny and it won't matter. - # We only search for the 'patterns' timeout or EOF, which aren't in - # the text itself. - #child = pexpect.spawn(pcmd, searchwindowsize=1) - if hasattr(pexpect, 'spawnb'): - child = pexpect.spawnb(self.sh, args=['-c', cmd]) # Pexpect-U - else: - child = pexpect.spawn(self.sh, args=['-c', cmd]) # Vanilla Pexpect - flush = sys.stdout.flush - while True: - # res is the index of the pattern that caused the match, so we - # know whether we've finished (if we matched EOF) or not - res_idx = child.expect_list(patterns, self.read_timeout) - print(child.before[out_size:].decode(enc, 'replace'), end='') - flush() - if res_idx==EOF_index: - break - # Update the pointer to what we've already printed - out_size = len(child.before) - except KeyboardInterrupt: - # We need to send ^C to the process. The ascii code for '^C' is 3 - # (the character is known as ETX for 'End of Text', see - # curses.ascii.ETX). - child.sendline(chr(3)) - # Read and print any more output the program might produce on its - # way out. - try: - out_size = len(child.before) - child.expect_list(patterns, self.terminate_timeout) - print(child.before[out_size:].decode(enc, 'replace'), end='') - sys.stdout.flush() - except KeyboardInterrupt: - # Impatient users tend to type it multiple times - pass - finally: - # Ensure the subprocess really is terminated - child.terminate(force=True) - # add isalive check, to ensure exitstatus is set: - child.isalive() - - # We follow the subprocess pattern, returning either the exit status - # as a positive number, or the terminating signal as a negative - # number. - # on Linux, sh returns 128+n for signals terminating child processes on Linux - # on BSD (OS X), the signal code is set instead - if child.exitstatus is None: - # on WIFSIGNALED, pexpect sets signalstatus, leaving exitstatus=None - if child.signalstatus is None: - # this condition may never occur, - # but let's be certain we always return an integer. - return 0 - return -child.signalstatus - if child.exitstatus > 128: - return -(child.exitstatus - 128) - return child.exitstatus - - -# Make system() with a functional interface for outside use. Note that we use -# getoutput() from the _common utils, which is built on top of popen(). Using -# pexpect to get subprocess output produces difficult to parse output, since -# programs think they are talking to a tty and produce highly formatted output -# (ls is a good example) that makes them hard. -system = ProcessHandler().system - -def check_pid(pid): - try: - os.kill(pid, 0) - except OSError as err: - if err.errno == errno.ESRCH: - return False - elif err.errno == errno.EPERM: - # Don't have permission to signal the process - probably means it exists - return True - raise - else: - return True +"""Posix-specific implementation of process utilities. + +This file is only meant to be imported by process.py, not by end-users. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2010-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib +import errno +import os +import subprocess as sp +import sys + +import pexpect + +# Our own +from ._process_common import getoutput, arg_split +from IPython.utils import py3compat +from IPython.utils.encoding import DEFAULT_ENCODING + +#----------------------------------------------------------------------------- +# Function definitions +#----------------------------------------------------------------------------- + +def _find_cmd(cmd): + """Find the full path to a command using which.""" + + path = sp.Popen(['/usr/bin/env', 'which', cmd], + stdout=sp.PIPE, stderr=sp.PIPE).communicate()[0] + return py3compat.decode(path) + + +class ProcessHandler(object): + """Execute subprocesses under the control of pexpect. + """ + # Timeout in seconds to wait on each reading of the subprocess' output. + # This should not be set too low to avoid cpu overusage from our side, + # since we read in a loop whose period is controlled by this timeout. + read_timeout = 0.05 + + # Timeout to give a process if we receive SIGINT, between sending the + # SIGINT to the process and forcefully terminating it. + terminate_timeout = 0.2 + + # File object where stdout and stderr of the subprocess will be written + logfile = None + + # Shell to call for subprocesses to execute + _sh = None + + @property + def sh(self): + if self._sh is None: + shell_name = os.environ.get("SHELL", "sh") + self._sh = pexpect.which(shell_name) + if self._sh is None: + raise OSError('"{}" shell not found'.format(shell_name)) + + return self._sh + + def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None): + """Arguments are used for pexpect calls.""" + self.read_timeout = (ProcessHandler.read_timeout if read_timeout is + None else read_timeout) + self.terminate_timeout = (ProcessHandler.terminate_timeout if + terminate_timeout is None else + terminate_timeout) + self.logfile = sys.stdout if logfile is None else logfile + + def getoutput(self, cmd): + """Run a command and return its stdout/stderr as a string. + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + output : str + A string containing the combination of stdout and stderr from the + subprocess, in whatever order the subprocess originally wrote to its + file descriptors (so the order of the information in this string is the + correct order as would be seen if running the command in a terminal). + """ + try: + return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n') + except KeyboardInterrupt: + print('^C', file=sys.stderr, end='') + + def getoutput_pexpect(self, cmd): + """Run a command and return its stdout/stderr as a string. + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + output : str + A string containing the combination of stdout and stderr from the + subprocess, in whatever order the subprocess originally wrote to its + file descriptors (so the order of the information in this string is the + correct order as would be seen if running the command in a terminal). + """ + try: + return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n') + except KeyboardInterrupt: + print('^C', file=sys.stderr, end='') + + def system(self, cmd): + """Execute a command in a subshell. + + Parameters + ---------- + cmd : str + A command to be executed in the system shell. + + Returns + ------- + int : child's exitstatus + """ + # Get likely encoding for the output. + enc = DEFAULT_ENCODING + + # Patterns to match on the output, for pexpect. We read input and + # allow either a short timeout or EOF + patterns = [pexpect.TIMEOUT, pexpect.EOF] + # the index of the EOF pattern in the list. + # even though we know it's 1, this call means we don't have to worry if + # we change the above list, and forget to change this value: + EOF_index = patterns.index(pexpect.EOF) + # The size of the output stored so far in the process output buffer. + # Since pexpect only appends to this buffer, each time we print we + # record how far we've printed, so that next time we only print *new* + # content from the buffer. + out_size = 0 + try: + # Since we're not really searching the buffer for text patterns, we + # can set pexpect's search window to be tiny and it won't matter. + # We only search for the 'patterns' timeout or EOF, which aren't in + # the text itself. + #child = pexpect.spawn(pcmd, searchwindowsize=1) + if hasattr(pexpect, 'spawnb'): + child = pexpect.spawnb(self.sh, args=['-c', cmd]) # Pexpect-U + else: + child = pexpect.spawn(self.sh, args=['-c', cmd]) # Vanilla Pexpect + flush = sys.stdout.flush + while True: + # res is the index of the pattern that caused the match, so we + # know whether we've finished (if we matched EOF) or not + res_idx = child.expect_list(patterns, self.read_timeout) + print(child.before[out_size:].decode(enc, 'replace'), end='') + flush() + if res_idx==EOF_index: + break + # Update the pointer to what we've already printed + out_size = len(child.before) + except KeyboardInterrupt: + # We need to send ^C to the process. The ascii code for '^C' is 3 + # (the character is known as ETX for 'End of Text', see + # curses.ascii.ETX). + child.sendline(chr(3)) + # Read and print any more output the program might produce on its + # way out. + try: + out_size = len(child.before) + child.expect_list(patterns, self.terminate_timeout) + print(child.before[out_size:].decode(enc, 'replace'), end='') + sys.stdout.flush() + except KeyboardInterrupt: + # Impatient users tend to type it multiple times + pass + finally: + # Ensure the subprocess really is terminated + child.terminate(force=True) + # add isalive check, to ensure exitstatus is set: + child.isalive() + + # We follow the subprocess pattern, returning either the exit status + # as a positive number, or the terminating signal as a negative + # number. + # on Linux, sh returns 128+n for signals terminating child processes on Linux + # on BSD (OS X), the signal code is set instead + if child.exitstatus is None: + # on WIFSIGNALED, pexpect sets signalstatus, leaving exitstatus=None + if child.signalstatus is None: + # this condition may never occur, + # but let's be certain we always return an integer. + return 0 + return -child.signalstatus + if child.exitstatus > 128: + return -(child.exitstatus - 128) + return child.exitstatus + + +# Make system() with a functional interface for outside use. Note that we use +# getoutput() from the _common utils, which is built on top of popen(). Using +# pexpect to get subprocess output produces difficult to parse output, since +# programs think they are talking to a tty and produce highly formatted output +# (ls is a good example) that makes them hard. +system = ProcessHandler().system + +def check_pid(pid): + try: + os.kill(pid, 0) + except OSError as err: + if err.errno == errno.ESRCH: + return False + elif err.errno == errno.EPERM: + # Don't have permission to signal the process - probably means it exists + return True + raise + else: + return True |