def run_in_thread(tox_env, os_env): res = None env_name = tox_env.envconfig.envname try: os_env[str(PARALLEL_ENV_VAR_KEY)] = str(env_name) args_sub = list(args) if hasattr(tox_env, "package"): args_sub.insert(position, str(tox_env.package)) args_sub.insert(position, "--installpkg") process = subprocess.Popen( args_sub, env=os_env, stdout=sink, stderr=sink, stdin=None, universal_newlines=True, ) res = process.wait() finally: semaphore.release() finished.set() tox_env.status = ( "skipped tests" if config.option.notest else ("parallel child exit code {}".format(res) if res else res) ) done.add(env_name) outcome = spinner.succeed if config.option.notest: outcome = spinner.skip elif res: outcome = spinner.fail outcome(env_name) if not live_out: out, err = process.communicate() if res or tox_env.envconfig.parallel_show_output: outcome = ( "Failed {} under process {}, stdout:\n".format(env_name, process.pid) if res else "" ) message = "{}{}{}".format( outcome, out, "\nstderr:\n{}".format(err) if err else "" ).rstrip() reporter.quiet(message)
def popen( self, args, cwd=None, env=None, redirect=True, returnout=False, ignore_ret=False, capture_err=True, callback=None, report_fail=True, ): """this drives an interaction with a subprocess""" cwd = py.path.local() if cwd is None else cwd cmd_args = [str(x) for x in self._rewrite_args(cwd, args)] cmd_args_shell = " ".join(pipes.quote(i) for i in cmd_args) stream_getter = self._get_standard_streams( capture_err, cmd_args_shell, redirect, returnout, cwd, ) exit_code, output = None, None with stream_getter as (fin, out_path, stderr, stdout): try: process = self.via_popen( cmd_args, stdout=stdout, stderr=stderr, cwd=str(cwd), env=os.environ.copy() if env is None else env, universal_newlines=True, shell=False, creationflags=( subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0 # needed for Windows signal send ability (CTRL+C) ), ) except OSError as exception: exit_code = exception.errno else: if callback is not None: callback(process) reporter.log_popen(cwd, out_path, cmd_args_shell, process.pid) output = self.evaluate_cmd(fin, process, redirect) exit_code = process.returncode finally: if out_path is not None and out_path.exists(): lines = out_path.read_text("UTF-8").split("\n") # first three lines are the action, cwd, and cmd - remove it output = "\n".join(lines[3:]) try: if exit_code and not ignore_ret: if report_fail: msg = "invocation failed (exit code {:d})".format( exit_code) if out_path is not None: msg += ", logfile: {}".format(out_path) if not out_path.exists(): msg += " warning log file missing" reporter.error(msg) if out_path is not None and out_path.exists(): reporter.separator("=", "log start", Verbosity.QUIET) reporter.quiet(output) reporter.separator("=", "log end", Verbosity.QUIET) raise InvocationError(cmd_args_shell, exit_code, output) finally: self.command_log.add_command(cmd_args, output, exit_code) return output