def _run_command(args, stdout=_PIPE, stderr=_PIPE, encoding=None, stream=0): # regarding the shell argument, see: http://bugs.python.org/issue8557 try: proc = _Popen(args, stdout=stdout, stderr=stderr, shell=(sys.platform == "win32")) data = proc.communicate()[stream] except OSError: return 1, "" # doubled checked and data = decode_as_string(data, encoding) # communciate calls wait() return proc.returncode, data
def _run_command(args, stdout=_PIPE, stderr=_PIPE): # regarding the shell argument, see: http://bugs.python.org/issue8557 try: args = [fsdecode(x) for x in args] proc = _Popen(args, stdout=stdout, stderr=stderr, shell=(sys.platform == "win32")) data = proc.communicate()[0] except OSError: return 1, "" data = consoledecode(data) # communciate calls wait() return proc.returncode, data
def run_setup_py(cmd, pypath=None, path=None, data_stream=0, env=None): """ Execution command for tests, separate from those used by the code directly to prevent accidental behavior issues """ if env is None: env = dict() for envname in os.environ: env[envname] = os.environ[envname] # override the python path if needed if pypath is not None: env["PYTHONPATH"] = pypath # overide the execution path if needed if path is not None: env["PATH"] = path if not env.get("PATH", ""): env["PATH"] = _which_dirs("tar").union(_which_dirs("gzip")) env["PATH"] = os.pathsep.join(env["PATH"]) cmd = [sys.executable, "setup.py"] + list(cmd) # http://bugs.python.org/issue8557 shell = sys.platform == 'win32' try: proc = _Popen( cmd, stdout=_PIPE, stderr=_PIPE, shell=shell, env=env, ) data = proc.communicate()[data_stream] except OSError: return 1, '' # decode the console string if needed if hasattr(data, "decode"): # use the default encoding data = data.decode() data = unicodedata.normalize('NFC', data) # communciate calls wait() return proc.returncode, data
def run(cmd, cwd=None, stdin=None, out=None, err=None, env=None, timeout=None, exp=0): """ Run a command and return (exit_code, std_out, std_err). Cmd: str or list of str. Cwd: str path. Stdin: str, bytes, open binary file (including value of dev_null()). Out, err: open binary file or _pipe special. Env: dict of str. Timeout: numeric or None. Exp: expected exit code can be None (accept any value), an integer code, or `...` (Ellipsis) to indicate any nonzero code. The special ellipsis notation is used because a bool expectation is confusing; nonzero implies True in Python, but False in Unix. The underlying Subprocess shell option is not supported because the rules regarding splitting strings are complex. User code is made clearer by just specifying the complete shell command; lists are used as is, while strings are split by shlex.split. """ if isinstance(cmd, str): cmd = _shlex.split(cmd) if isinstance(stdin, str): f_in = _pipe input_bytes = stdin.encode("utf-8") elif isinstance(stdin, bytes): f_in = _pipe input_bytes = stdin else: f_in = stdin # presume None, _pipe, or file, which includes dev_null(). input_bytes = None proc = _Popen(cmd, cwd=cwd, stdin=f_in, stdout=out, stderr=err, shell=False, env=env) # timeout alarm handler. timed_out = False if timeout is not None: def alarm_handler(signum, current_stack_frame): # since signal handlers carry reentrancy concerns, do not do any IO within the handler. nonlocal timed_out timed_out = True proc.kill() signal.signal(signal.SIGALRM, alarm_handler) # set handler. signal.alarm(timeout) # set alarm. p_out, p_err = proc.communicate(input_bytes) # waits for process to complete. if timeout is not None: signal.alarm(0) # disable alarm. if timed_out: raise ProcessTimeout(cmd, timeout) code = proc.returncode if exp is None: pass elif exp is Ellipsis: if code == 0: raise ProcessExpectation(cmd, "!= 0", code) else: if code != exp: # otherwise expect exact numeric code. raise ProcessExpectation(cmd, exp, code) return code, _decode(p_out), _decode(p_err)
def shell_run(cmd, cin=None, cwd=None, timeout=10, critical=True, verbose=True): ''' Runs a shell command within a controlled environment. .. note:: |use_photon_m| :param cmd: The command to run * A string one would type into a console like \ :command:`git push -u origin master`. * Will be split using :py:func:`shlex.split`. * It is possible to use a list here, but then no splitting is done. :param cin: Add something to stdin of `cmd` :param cwd: Run `cmd` insde specified current working directory :param timeout: Catch infinite loops (e.g. ``ping``). Exit after `timeout` seconds :param critical: If set to ``True``: |appteardown| on failure of `cmd` :param verbose: Show messages and warnings :returns: A dictionary containing the results from running `cmd` with the following: * 'command': `cmd` * 'stdin': `cin` (If data was set in `cin`) * 'cwd': `cwd` (If `cwd` was set) * 'exception': exception message (If an exception was thrown) * 'timeout': `timeout` (If a timeout exception was thrown) * 'stdout': List from stdout (If any) * 'stderr': List from stderr (If any) * 'returncode': The returncode (If not any exception) * 'out': The most urgent message as joined string. \ ('exception' > 'stderr' > 'stdout') ''' res = dict(command=cmd) if cin: cin = str(cin) res.update(dict(stdin=cin)) if cwd: res.update(dict(cwd=cwd)) if isinstance(cmd, str): cmd = _split(cmd) try: p = _Popen( cmd, stdin=_PIPE, stdout=_PIPE, stderr=_PIPE, bufsize=1, cwd=cwd, universal_newlines=True ) except Exception as ex: res.update(dict(exception=str(ex))) else: try: out, err = p.communicate(input=cin, timeout=timeout) if out: res.update(dict( stdout=[o for o in out.split('\n') if o] )) if err: res.update(dict( stderr=[e for e in err.split('\n') if e] )) res.update(dict(returncode=p.returncode)) except _TimeoutExpired as ex: res.update(dict(exception=str(ex), timeout=timeout)) p.kill() except Exception as ex: res.update(dict(exception=str(ex))) res.update( out=( res.get('exception') or '\n'.join(res.get('stderr') or res.get('stdout', '')) ) ) if res.get('returncode', -1) != 0: res.update(dict(critical=critical)) shell_notify( 'error in shell command \'%s\'' % (res.get('command')), state=True if critical else None, more=res, verbose=verbose ) return res
def verify_python3_env(): """Ensures that the environment is good for unicode on Python 3.""" try: fs_enc = _codecs.lookup(_locale.getpreferredencoding()).name except Exception: fs_enc = 'ascii' if fs_enc != 'ascii': return extra = '' if _os.name == 'posix': rv = _Popen( ['locale', '-a'], stdout=_PIPE, stderr=_PIPE).communicate()[0] good_locales = set() has_c_utf8 = False # Make sure we're operating on text here. if isinstance(rv, bytes): rv = rv.decode('ascii', 'replace') for line in rv.splitlines(): locale = line.strip() if locale.lower().endswith(('.utf-8', '.utf8')): good_locales.add(locale) if locale.lower() in ('c.utf8', 'c.utf-8'): has_c_utf8 = True extra += '\n\n' if not good_locales: extra += ( 'Additional information: on this system no suitable UTF-8\n' 'locales were discovered. This most likely requires resolving\n' 'by reconfiguring the locale system.') elif has_c_utf8: extra += ( 'This system supports the C.UTF-8 locale which is recommended.\n' 'You might be able to resolve your issue by exporting the\n' 'following environment variables:\n\n' ' export LC_ALL=C.UTF-8\n' ' export LANG=C.UTF-8') else: extra += ( 'This system lists a couple of UTF-8 supporting locales that\n' 'you can pick from. The following suitable locales were\n' 'discovered: %s') % ', '.join(sorted(good_locales)) bad_locale = None for locale in _os.environ.get('LC_ALL'), _os.environ.get('LANG'): if locale and locale.lower().endswith(('.utf-8', '.utf8')): bad_locale = locale if locale is not None: break if bad_locale is not None: extra += ( '\n\nnpm2deb discovered that you exported a UTF-8 locale\n' 'but the locale system could not pick up from it because\n' 'it does not exist. The exported locale is "%s" but it\n' 'is not supported') % bad_locale raise RuntimeError('npm2deb will abort further execution because Python 3 ' 'was configured to use ASCII as encoding for the ' 'environment.' + extra)