def _reset(self, config, popen=subprocess.Popen): self.config = config self.popen = popen self.resultlog = ResultLog() self.existing_venvs = OrderedDict() self.venv_dict = {} if self.config.run_provision else self._build_venvs( )
class Session(object): """The session object that ties together configuration, reporting, venv creation, testing.""" def __init__(self, config, popen=subprocess.Popen): self._reset(config, popen) def _reset(self, config, popen=subprocess.Popen): self.config = config self.popen = popen self.resultlog = ResultLog() self.existing_venvs = OrderedDict() self.venv_dict = {} if self.config.run_provision else self._build_venvs( ) def _build_venvs(self): try: need_to_run = OrderedDict( (v, self.getvenv(v)) for v in self._evaluated_env_list) try: venv_order = stable_topological_sort( OrderedDict((name, v.envconfig.depends) for name, v in need_to_run.items()), ) venvs = OrderedDict((v, need_to_run[v]) for v in venv_order) return venvs except ValueError as exception: reporter.error( "circular dependency detected: {}".format(exception)) except LookupError: pass except tox.exception.ConfigError as exception: reporter.error(str(exception)) raise SystemExit(1) def getvenv(self, name): if name in self.existing_venvs: return self.existing_venvs[name] env_config = self.config.envconfigs.get(name, None) if env_config is None: reporter.error("unknown environment {!r}".format(name)) raise LookupError(name) elif env_config.envdir == self.config.toxinidir: reporter.error("venv {!r} in {} would delete project".format( name, env_config.envdir)) raise tox.exception.ConfigError("envdir must not equal toxinidir") env_log = self.resultlog.get_envlog(name) venv = VirtualEnv(envconfig=env_config, popen=self.popen, env_log=env_log) self.existing_venvs[name] = venv return venv @property def _evaluated_env_list(self): tox_env_filter = os.environ.get("TOX_SKIP_ENV") tox_env_filter_re = (re.compile(tox_env_filter) if tox_env_filter is not None else None) visited = set() for name in self.config.envlist: if name in visited: continue visited.add(name) if tox_env_filter_re is not None and tox_env_filter_re.match(name): msg = "skip environment {}, matches filter {!r}".format( name, tox_env_filter_re.pattern, ) reporter.verbosity1(msg) continue yield name @property def hook(self): return self.config.pluginmanager.hook def newaction(self, name, msg, *args): return Action( name, msg, args, self.config.logdir, self.config.option.resultjson, self.resultlog.command_log, self.popen, sys.executable, SUICIDE_TIMEOUT, INTERRUPT_TIMEOUT, TERMINATE_TIMEOUT, ) def runcommand(self): reporter.using( "tox-{} from {} (pid {})".format(tox.__version__, tox.__file__, os.getpid()), ) show_description = reporter.has_level(reporter.Verbosity.DEFAULT) if self.config.run_provision: provision_tox_venv = self.getvenv(self.config.provision_tox_env) return provision_tox(provision_tox_venv, self.config.args) else: if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs(all_envs=False, description=show_description) elif self.config.option.listenvs_all: self.showenvs(all_envs=True, description=show_description) else: with self.cleanup(): return self.subcommand_test() @contextmanager def cleanup(self): self.config.temp_dir.ensure(dir=True) try: yield finally: self.hook.tox_cleanup(session=self) def subcommand_test(self): if self.config.skipsdist: reporter.info("skipping sdist step") else: for venv in self.venv_dict.values(): if not venv.envconfig.skip_install: venv.package = self.hook.tox_package(session=self, venv=venv) if not venv.package: return 2 venv.envconfig.setenv[str("TOX_PACKAGE")] = str( venv.package) if self.config.option.sdistonly: return within_parallel = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ try: if not within_parallel and self.config.option.parallel != PARALLEL_OFF: run_parallel(self.config, self.venv_dict) else: run_sequential(self.config, self.venv_dict) finally: retcode = self._summary() return retcode def _add_parallel_summaries(self): if (self.config.option.parallel != PARALLEL_OFF and "testenvs" in self.resultlog.dict): result_log = self.resultlog.dict["testenvs"] for tox_env in self.venv_dict.values(): data = self._load_parallel_env_report(tox_env) if data and "testenvs" in data and tox_env.name in data[ "testenvs"]: result_log[tox_env.name] = data["testenvs"][tox_env.name] @staticmethod def _load_parallel_env_report(tox_env): """Load report data into memory, remove disk file""" result_json_path = tox_env.get_result_json_path() if result_json_path and result_json_path.exists(): with result_json_path.open("r") as file_handler: data = json.load(file_handler) result_json_path.remove() return data def _summary(self): is_parallel_child = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ if not is_parallel_child: reporter.separator("_", "summary", reporter.Verbosity.QUIET) exit_code = 0 for venv in self.venv_dict.values(): report = reporter.good status = getattr(venv, "status", "undefined") if isinstance(status, tox.exception.InterpreterNotFound): msg = " {}: {}".format(venv.envconfig.envname, str(status)) if self.config.option.skip_missing_interpreters == "true": report = reporter.skip else: exit_code = 1 report = reporter.error elif status == "platform mismatch": msg = " {}: {} ({!r} does not match {!r})".format( venv.envconfig.envname, str(status), sys.platform, venv.envconfig.platform, ) report = reporter.skip elif status and status == "ignored failed command": msg = " {}: {}".format(venv.envconfig.envname, str(status)) elif status and status != "skipped tests": msg = " {}: {}".format(venv.envconfig.envname, str(status)) report = reporter.error exit_code = 1 else: if not status: status = "commands succeeded" msg = " {}: {}".format(venv.envconfig.envname, status) if not is_parallel_child: report(msg) if not exit_code and not is_parallel_child: reporter.good(" congratulations :)") path = self.config.option.resultjson if path: if not is_parallel_child: self._add_parallel_summaries() path = py.path.local(path) data = self.resultlog.dumps_json() reporter.line("write json report at: {}".format(path)) path.write(data) return exit_code def showconfig(self): show_config(self.config) def showenvs(self, all_envs=False, description=False): show_envs(self.config, all_envs=all_envs, description=description)