def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) self.report.using("tox.ini: {}".format(self.config.toxinipath)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [ self.getvenv(x) for x in self.evaluated_env_list() ] except LookupError: raise SystemExit(1) except tox.exception.ConfigError as exception: self.report.error(str(exception)) raise SystemExit(1) try: self.venv_order = stable_topological_sort( OrderedDict( (v.name, v.envconfig.depends) for v in self.venvlist)) except ValueError as exception: self.report.error( "circular dependency detected: {}".format(exception)) raise SystemExit(1) self._actions = []
def test_addenv_setpython(pkg): replog = ResultLog() replog.set_header(installpkg=pkg) envlog = replog.get_envlog("py26") envlog.set_python_info(py.path.local(sys.executable)) assert envlog.dict["python"]["version_info"] == list(sys.version_info) assert envlog.dict["python"]["version"] == sys.version assert envlog.dict["python"]["executable"] == sys.executable
def test_addenv_setpython(pkg): replog = ResultLog() replog.set_header(installpkg=pkg) envlog = replog.get_envlog("py36") envlog.set_python_info(py.path.local(sys.executable)) assert envlog.dict["python"]["version_info"] == list(sys.version_info) assert envlog.dict["python"]["version"] == sys.version assert envlog.dict["python"]["executable"] == sys.executable
def test_pre_set_header(pkg): replog = ResultLog() d = replog.dict assert replog.dict == d assert replog.dict["reportversion"] == "1" assert replog.dict["toxversion"] == tox.__version__ assert replog.dict["platform"] == sys.platform assert replog.dict["host"] == socket.getfqdn() data = replog.dumps_json() replog2 = ResultLog.loads_json(data) assert replog2.dict == replog.dict
def test_pre_set_header(pkg): replog = ResultLog() d = replog.dict assert replog.dict == d assert replog.dict["reportversion"] == "1" assert replog.dict["toxversion"] == tox.__version__ assert replog.dict["platform"] == sys.platform assert replog.dict["host"] == py.std.socket.getfqdn() data = replog.dumps_json() replog2 = ResultLog.loads_json(data) assert replog2.dict == replog.dict
def test_get_commandlog(pkg): replog = ResultLog() replog.set_header(installpkg=pkg) envlog = replog.get_envlog("py26") assert "setup" not in envlog.dict setuplog = envlog.get_commandlog("setup") setuplog.add_command(["virtualenv", "..."], "venv created", 0) assert setuplog.list == [{"command": ["virtualenv", "..."], "output": "venv created", "retcode": "0"}] assert envlog.dict["setup"] setuplog2 = replog.get_envlog("py26").get_commandlog("setup") assert setuplog2.list == setuplog.list
def test_set_header(pkg): replog = ResultLog() d = replog.dict replog.set_header(installpkg=pkg) assert replog.dict == d assert replog.dict["reportversion"] == "1" assert replog.dict["toxversion"] == tox.__version__ assert replog.dict["platform"] == sys.platform assert replog.dict["host"] == py.std.socket.getfqdn() assert replog.dict["installpkg"] == {"basename": "hello-1.0.tar.gz", "md5": pkg.computehash("md5"), "sha256": pkg.computehash("sha256")} data = replog.dumps_json() replog2 = ResultLog.loads_json(data) assert replog2.dict == replog.dict
def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) #self.report.using("logdir %s" %(self.config.logdir,)) self.report.using("tox.ini: %s" % (self.config.toxinipath, )) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [self.getvenv(x) for x in self.config.envlist] except LookupError: raise SystemExit(1) self._actions = []
def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) self.report.using("tox.ini: {}".format(self.config.toxinipath)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [self.getvenv(x) for x in self.evaluated_env_list()] except LookupError: raise SystemExit(1) except tox.exception.ConfigError as e: self.report.error(str(e)) raise SystemExit(1) self._actions = []
def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) #self.report.using("logdir %s" %(self.config.logdir,)) self.report.using("tox.ini: %s" %(self.config.toxinipath,)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [self.getvenv(x) for x in self.config.envlist] except LookupError: raise SystemExit(1) self._actions = []
def test_get_commandlog(pkg): replog = ResultLog() replog.set_header(installpkg=pkg) envlog = replog.get_envlog("py36") assert "setup" not in envlog.dict setuplog = envlog.get_commandlog("setup") setuplog.add_command(["virtualenv", "..."], "venv created", 0) assert setuplog.list == [{ "command": ["virtualenv", "..."], "output": "venv created", "retcode": "0" }] assert envlog.dict["setup"] setuplog2 = replog.get_envlog("py36").get_commandlog("setup") assert setuplog2.list == setuplog.list
def test_set_header(pkg): replog = ResultLog() d = replog.dict replog.set_header(installpkg=pkg) assert replog.dict == d assert replog.dict["reportversion"] == "1" assert replog.dict["toxversion"] == tox.__version__ assert replog.dict["platform"] == sys.platform assert replog.dict["host"] == socket.getfqdn() assert replog.dict["installpkg"] == { "basename": "hello-1.0.tar.gz", "md5": pkg.computehash("md5"), "sha256": pkg.computehash("sha256") } data = replog.dumps_json() replog2 = ResultLog.loads_json(data) assert replog2.dict == replog.dict
class Session: def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) #self.report.using("logdir %s" %(self.config.logdir,)) self.report.using("tox.ini: %s" %(self.config.toxinipath,)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [self.getvenv(x) for x in self.config.envlist] except LookupError: raise SystemExit(1) self._actions = [] def _makevenv(self, name): envconfig = self.config.envconfigs.get(name, None) if envconfig is None: self.report.error("unknown environment %r" % name) raise LookupError(name) venv = VirtualEnv(envconfig=envconfig, session=self) self._name2venv[name] = venv return venv def getvenv(self, name): """ return a VirtualEnv controler object for the 'name' env. """ try: return self._name2venv[name] except KeyError: return self._makevenv(name) def newaction(self, venv, msg, *args): action = Action(self, venv, msg, args) self._actions.append(action) return action def runcommand(self): self.report.using("tox-%s from %s" %(tox.__version__, tox.__file__)) if self.config.minversion: minversion = NormalizedVersion(self.config.minversion) toxversion = NormalizedVersion(tox.__version__) if toxversion < minversion: self.report.error( "tox version is %s, required is at least %s" %( toxversion, minversion)) raise SystemExit(1) if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs() else: return self.subcommand_test() def _copyfiles(self, srcdir, pathlist, destdir): for relpath in pathlist: src = srcdir.join(relpath) if not src.check(): self.report.error("missing source file: %s" %(src,)) raise SystemExit(1) target = destdir.join(relpath) target.dirpath().ensure(dir=1) src.copy(target) def _makesdist(self): setup = self.config.setupdir.join("setup.py") if not setup.check(): raise tox.exception.MissingFile(setup) action = self.newaction(None, "packaging") with action: action.setactivity("sdist-make", setup) self.make_emptydir(self.config.distdir) action.popen([sys.executable, setup, "sdist", "--formats=zip", "--dist-dir", self.config.distdir, ], cwd=self.config.setupdir) try: return self.config.distdir.listdir()[0] except py.error.ENOENT: # check if empty or comment only data = [] with open(str(setup)) as fp: for line in fp: if line and line[0] == '#': continue data.append(line) if not ''.join(data).strip(): self.report.error( 'setup.py is empty' ) raise SystemExit(1) self.report.error( 'No dist directory found. Please check setup.py, e.g with:\n'\ ' python setup.py sdist' ) raise SystemExit(1) def make_emptydir(self, path): if path.check(): self.report.info(" removing %s" % path) py.std.shutil.rmtree(str(path), ignore_errors=True) path.ensure(dir=1) def setupenv(self, venv): action = self.newaction(venv, "getenv", venv.envconfig.envdir) with action: venv.status = 0 envlog = self.resultlog.get_envlog(venv.name) try: status = venv.update(action=action) except tox.exception.InvocationError: status = sys.exc_info()[1] if status: commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str(status), 1) venv.status = status self.report.error(str(status)) return False commandpath = venv.getcommandpath("python") envlog.set_python_info(commandpath) return True def finishvenv(self, venv): action = self.newaction(venv, "finishvenv") with action: venv.finish() return True def developpkg(self, venv, setupdir): action = self.newaction(venv, "developpkg", setupdir) with action: try: venv.developpkg(setupdir, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def installpkg(self, venv, sdist_path): self.resultlog.set_header(installpkg=sdist_path) action = self.newaction(venv, "installpkg", sdist_path) with action: try: venv.installpkg(sdist_path, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def sdist(self): if not self.config.option.sdistonly and (self.config.sdistsrc or self.config.option.installpkg): sdist_path = self.config.option.installpkg if not sdist_path: sdist_path = self.config.sdistsrc sdist_path = self._resolve_pkg(sdist_path) self.report.info("using package %r, skipping 'sdist' activity " % str(sdist_path)) else: try: sdist_path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] self.report.error("FAIL could not package project") return sdistfile = self.config.distshare.join(sdist_path.basename) if sdistfile != sdist_path: self.report.info("copying new sdistfile to %r" % str(sdistfile)) try: sdistfile.dirpath().ensure(dir=1) except py.error.Error: self.report.warning("could not copy distfile to %s" % sdistfile.dirpath()) else: sdist_path.copy(sdistfile) return sdist_path def subcommand_test(self): if self.config.skipsdist: self.report.info("skipping sdist step") sdist_path = None else: sdist_path = self.sdist() if not sdist_path: return 2 if self.config.option.sdistonly: return for venv in self.venvlist: if self.setupenv(venv): if venv.envconfig.develop: self.developpkg(venv, self.config.setupdir) elif self.config.skipsdist: self.finishvenv(venv) else: self.installpkg(venv, sdist_path) self.runtestenv(venv) retcode = self._summary() return retcode def runtestenv(self, venv, redirect=False): if not self.config.option.notest: if venv.status: return venv.test(redirect=redirect) else: venv.status = "skipped tests" def _summary(self): self.report.startsummary() retcode = 0 for venv in self.venvlist: status = venv.status if status and status != "skipped tests": msg = " %s: %s" %(venv.envconfig.envname, str(status)) self.report.error(msg) retcode = 1 else: if not status: status = "commands succeeded" self.report.good(" %s: %s" %(venv.envconfig.envname, status)) if not retcode: self.report.good(" congratulations :)") path = self.config.option.resultjson if path: path = py.path.local(path) path.write(self.resultlog.dumps_json()) self.report.line("wrote json report at: %s" % path) return retcode def showconfig(self): self.info_versions() self.report.keyvalue("config-file:", self.config.option.configfile) self.report.keyvalue("toxinipath: ", self.config.toxinipath) self.report.keyvalue("toxinidir: ", self.config.toxinidir) self.report.keyvalue("toxworkdir: ", self.config.toxworkdir) self.report.keyvalue("setupdir: ", self.config.setupdir) self.report.keyvalue("distshare: ", self.config.distshare) self.report.keyvalue("skipsdist: ", self.config.skipsdist) self.report.tw.line() for envconfig in self.config.envconfigs.values(): self.report.line("[testenv:%s]" % envconfig.envname, bold=True) self.report.line(" basepython=%s" % envconfig.basepython) self.report.line(" _basepython_info=%s" % envconfig._basepython_info) self.report.line(" envpython=%s" % envconfig.envpython) self.report.line(" envtmpdir=%s" % envconfig.envtmpdir) self.report.line(" envbindir=%s" % envconfig.envbindir) self.report.line(" envlogdir=%s" % envconfig.envlogdir) self.report.line(" changedir=%s" % envconfig.changedir) self.report.line(" args_are_path=%s" % envconfig.args_are_paths) self.report.line(" install_command=%s" % envconfig.install_command) self.report.line(" commands=") for command in envconfig.commands: self.report.line(" %s" % command) self.report.line(" deps=%s" % envconfig.deps) self.report.line(" envdir= %s" % envconfig.envdir) self.report.line(" downloadcache=%s" % envconfig.downloadcache) self.report.line(" usedevelop=%s" % envconfig.develop) def showenvs(self): for env in self.config.envlist: self.report.line("%s" % env) def info_versions(self): versions = ['tox-%s' % tox.__version__] try: version = py.process.cmdexec("virtualenv --version") except py.process.cmdexec.Error: versions.append("virtualenv-1.9.1 (vendored)") else: versions.append("virtualenv-%s" % version.strip()) self.report.keyvalue("tool-versions:", " ".join(versions)) def _resolve_pkg(self, pkgspec): try: return self._spec2pkg[pkgspec] except KeyError: self._spec2pkg[pkgspec] = x = self._resolvepkg(pkgspec) return x def _resolvepkg(self, pkgspec): if not os.path.isabs(str(pkgspec)): return pkgspec p = py.path.local(pkgspec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) self.report.info("determining %s" % p) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox.exception.MissingDependency(pkgspec) if len(candidates) > 1: items = [] for x in candidates: ver = getversion(x.basename) if ver is not None: items.append((ver, x)) else: self.report.warning("could not determine version of: %s" % str(x)) items.sort() if not items: raise tox.exception.MissingDependency(pkgspec) return items[-1][1] else: return candidates[0]
class Session: """ (unstable API). the session object that ties together configuration, reporting, venv creation, testing. """ def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) self.report.using("tox.ini: %s" % (self.config.toxinipath,)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [ self.getvenv(x) for x in self.config.envlist ] except LookupError: raise SystemExit(1) except tox.exception.ConfigError as e: self.report.error(str(e)) raise SystemExit(1) self._actions = [] @property def hook(self): return self.config.pluginmanager.hook def _makevenv(self, name): envconfig = self.config.envconfigs.get(name, None) if envconfig is None: self.report.error("unknown environment %r" % name) raise LookupError(name) elif envconfig.envdir == self.config.toxinidir: self.report.error( "venv %r in %s would delete project" % (name, envconfig.envdir)) raise tox.exception.ConfigError('envdir must not equal toxinidir') venv = VirtualEnv(envconfig=envconfig, session=self) self._name2venv[name] = venv return venv def getvenv(self, name): """ return a VirtualEnv controler object for the 'name' env. """ try: return self._name2venv[name] except KeyError: return self._makevenv(name) def newaction(self, venv, msg, *args): action = Action(self, venv, msg, args) self._actions.append(action) return action def runcommand(self): self.report.using("tox-%s from %s" % (tox.__version__, tox.__file__)) verbosity = (self.report.verbosity > Verbosity.DEFAULT) if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs(all_envs=False, description=verbosity) elif self.config.option.listenvs_all: self.showenvs(all_envs=True, description=verbosity) else: return self.subcommand_test() def _copyfiles(self, srcdir, pathlist, destdir): for relpath in pathlist: src = srcdir.join(relpath) if not src.check(): self.report.error("missing source file: %s" % (src,)) raise SystemExit(1) target = destdir.join(relpath) target.dirpath().ensure(dir=1) src.copy(target) def _makesdist(self): setup = self.config.setupdir.join("setup.py") if not setup.check(): self.report.error( "No setup.py file found. The expected location is:\n" " %s\n" "You can\n" " 1. Create one:\n" " https://packaging.python.org/tutorials/distributing-packages/#setup-py\n" " 2. Configure tox to avoid running sdist:\n" " http://tox.readthedocs.io/en/latest/example/general.html" "#avoiding-expensive-sdist" % setup ) raise SystemExit(1) action = self.newaction(None, "packaging") with action: action.setactivity("sdist-make", setup) self.make_emptydir(self.config.distdir) action.popen([sys.executable, setup, "sdist", "--formats=zip", "--dist-dir", self.config.distdir, ], cwd=self.config.setupdir) try: return self.config.distdir.listdir()[0] except py.error.ENOENT: # check if empty or comment only data = [] with open(str(setup)) as fp: for line in fp: if line and line[0] == '#': continue data.append(line) if not ''.join(data).strip(): self.report.error( 'setup.py is empty' ) raise SystemExit(1) self.report.error( 'No dist directory found. Please check setup.py, e.g with:\n' ' python setup.py sdist' ) raise SystemExit(1) def make_emptydir(self, path): if path.check(): self.report.info(" removing %s" % path) shutil.rmtree(str(path), ignore_errors=True) path.ensure(dir=1) def setupenv(self, venv): if venv.envconfig.missing_subs: venv.status = ( "unresolvable substitution(s): %s. " "Environment variables are missing or defined recursively." % (','.join(["'%s'" % m for m in venv.envconfig.missing_subs]))) return if not venv.matching_platform(): venv.status = "platform mismatch" return # we simply omit non-matching platforms action = self.newaction(venv, "getenv", venv.envconfig.envdir) with action: venv.status = 0 default_ret_code = 1 envlog = self.resultlog.get_envlog(venv.name) try: status = venv.update(action=action) except IOError as e: if e.args[0] != 2: raise status = ( "Error creating virtualenv. Note that spaces in paths are " "not supported by virtualenv. Error details: %r" % e) except tox.exception.InvocationError as e: status = ( "Error creating virtualenv. Note that some special " "characters (e.g. ':' and unicode symbols) in paths are " "not supported by virtualenv. Error details: %r" % e) except tox.exception.InterpreterNotFound as e: status = e if self.config.option.skip_missing_interpreters: default_ret_code = 0 if status: commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str(status), default_ret_code) venv.status = status if default_ret_code == 0: self.report.skip(str(status)) else: self.report.error(str(status)) return False commandpath = venv.getcommandpath("python") envlog.set_python_info(commandpath) return True def finishvenv(self, venv): action = self.newaction(venv, "finishvenv") with action: venv.finish() return True def developpkg(self, venv, setupdir): action = self.newaction(venv, "developpkg", setupdir) with action: try: venv.developpkg(setupdir, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def installpkg(self, venv, path): """Install package in the specified virtual environment. :param VenvConfig venv: Destination environment :param str path: Path to the distribution package. :return: True if package installed otherwise False. :rtype: bool """ self.resultlog.set_header(installpkg=py.path.local(path)) action = self.newaction(venv, "installpkg", path) with action: try: venv.installpkg(path, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def get_installpkg_path(self): """ :return: Path to the distribution :rtype: py.path.local """ if not self.config.option.sdistonly and (self.config.sdistsrc or self.config.option.installpkg): path = self.config.option.installpkg if not path: path = self.config.sdistsrc path = self._resolve_pkg(path) self.report.info("using package %r, skipping 'sdist' activity " % str(path)) else: try: path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] self.report.error("FAIL could not package project - v = %r" % v) return sdistfile = self.config.distshare.join(path.basename) if sdistfile != path: self.report.info("copying new sdistfile to %r" % str(sdistfile)) try: sdistfile.dirpath().ensure(dir=1) except py.error.Error: self.report.warning("could not copy distfile to %s" % sdistfile.dirpath()) else: path.copy(sdistfile) return path def subcommand_test(self): if self.config.skipsdist: self.report.info("skipping sdist step") path = None else: path = self.get_installpkg_path() if not path: return 2 if self.config.option.sdistonly: return for venv in self.venvlist: if self.setupenv(venv): if venv.envconfig.skip_install: self.finishvenv(venv) else: if venv.envconfig.usedevelop: self.developpkg(venv, self.config.setupdir) elif self.config.skipsdist: self.finishvenv(venv) else: self.installpkg(venv, path) # write out version dependency information action = self.newaction(venv, "envreport") with action: args = venv.envconfig.list_dependencies_command output = venv._pcall(args, cwd=self.config.toxinidir, action=action) # the output contains a mime-header, skip it output = output.split("\n\n")[-1] packages = output.strip().split("\n") action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) envlog.set_installed(packages) self.runtestenv(venv) retcode = self._summary() return retcode def runtestenv(self, venv, redirect=False): if not self.config.option.notest: if venv.status: return self.hook.tox_runtest_pre(venv=venv) self.hook.tox_runtest(venv=venv, redirect=redirect) self.hook.tox_runtest_post(venv=venv) else: venv.status = "skipped tests" def _summary(self): self.report.startsummary() retcode = 0 for venv in self.venvlist: status = venv.status if isinstance(status, tox.exception.InterpreterNotFound): msg = " %s: %s" % (venv.envconfig.envname, str(status)) if self.config.option.skip_missing_interpreters: self.report.skip(msg) else: retcode = 1 self.report.error(msg) elif status == "platform mismatch": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.skip(msg) elif status and status == "ignored failed command": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.good(msg) elif status and status != "skipped tests": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.error(msg) retcode = 1 else: if not status: status = "commands succeeded" self.report.good(" %s: %s" % (venv.envconfig.envname, status)) if not retcode: self.report.good(" congratulations :)") path = self.config.option.resultjson if path: path = py.path.local(path) path.write(self.resultlog.dumps_json()) self.report.line("wrote json report at: %s" % path) return retcode def showconfig(self): self.info_versions() self.report.keyvalue("config-file:", self.config.option.configfile) self.report.keyvalue("toxinipath: ", self.config.toxinipath) self.report.keyvalue("toxinidir: ", self.config.toxinidir) self.report.keyvalue("toxworkdir: ", self.config.toxworkdir) self.report.keyvalue("setupdir: ", self.config.setupdir) self.report.keyvalue("distshare: ", self.config.distshare) self.report.keyvalue("skipsdist: ", self.config.skipsdist) self.report.tw.line() for envconfig in self.config.envconfigs.values(): self.report.line("[testenv:%s]" % envconfig.envname, bold=True) for attr in self.config._parser._testenv_attr: self.report.line(" %-15s = %s" % (attr.name, getattr(envconfig, attr.name))) def showenvs(self, all_envs=False, description=False): env_conf = self.config.envconfigs # this contains all environments default = self.config.envlist # this only the defaults extra = sorted(e for e in env_conf if e not in default) if all_envs else [] if description: self.report.line('default environments:') max_length = max(len(env) for env in (default + extra)) def report_env(e): if description: text = env_conf[e].description or '[no description]' msg = '{} -> {}'.format(e.ljust(max_length), text).strip() else: msg = e self.report.line(msg) for e in default: report_env(e) if all_envs and extra: if description: self.report.line('') self.report.line('additional environments:') for e in extra: report_env(e) def info_versions(self): versions = ['tox-%s' % tox.__version__] proc = subprocess.Popen( (sys.executable, '-m', 'virtualenv', '--version'), stdout=subprocess.PIPE, ) out, _ = proc.communicate() versions.append('virtualenv-{}'.format(out.decode('UTF-8').strip())) self.report.keyvalue("tool-versions:", " ".join(versions)) def _resolve_pkg(self, pkgspec): try: return self._spec2pkg[pkgspec] except KeyError: self._spec2pkg[pkgspec] = x = self._resolvepkg(pkgspec) return x def _resolvepkg(self, pkgspec): if not os.path.isabs(str(pkgspec)): return pkgspec p = py.path.local(pkgspec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) self.report.info("determining %s" % p) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox.exception.MissingDependency(pkgspec) if len(candidates) > 1: items = [] for x in candidates: ver = getversion(x.basename) if ver is not None: items.append((ver, x)) else: self.report.warning("could not determine version of: %s" % str(x)) items.sort() if not items: raise tox.exception.MissingDependency(pkgspec) return items[-1][1] else: return candidates[0]
class Session: """The session object that ties together configuration, reporting, venv creation, testing.""" def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) self.report.using("tox.ini: {}".format(self.config.toxinipath)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [self.getvenv(x) for x in self.evaluated_env_list()] except LookupError: raise SystemExit(1) except tox.exception.ConfigError as e: self.report.error(str(e)) raise SystemExit(1) self._actions = [] 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 for name in self.config.envlist: 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 ) self.report.verbosity1(msg) continue yield name @property def hook(self): return self.config.pluginmanager.hook def _makevenv(self, name): envconfig = self.config.envconfigs.get(name, None) if envconfig is None: self.report.error("unknown environment {!r}".format(name)) raise LookupError(name) elif envconfig.envdir == self.config.toxinidir: self.report.error( "venv {!r} in {} would delete project".format(name, envconfig.envdir) ) raise tox.exception.ConfigError("envdir must not equal toxinidir") venv = VirtualEnv(envconfig=envconfig, session=self) self._name2venv[name] = venv return venv def getvenv(self, name): """ return a VirtualEnv controler object for the 'name' env. """ try: return self._name2venv[name] except KeyError: return self._makevenv(name) def newaction(self, venv, msg, *args): action = Action(self, venv, msg, args) self._actions.append(action) return action def runcommand(self): self.report.using("tox-{} from {}".format(tox.__version__, tox.__file__)) verbosity = self.report.verbosity > Verbosity.DEFAULT if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs(all_envs=False, description=verbosity) elif self.config.option.listenvs_all: self.showenvs(all_envs=True, description=verbosity) else: with self.cleanup(): return self.subcommand_test() @contextmanager def cleanup(self): self.config.temp_dir.ensure(dir=True) try: yield finally: for tox_env in self.venvlist: if ( hasattr(tox_env, "package") and isinstance(tox_env.package, py.path.local) and tox_env.package.exists() ): self.report.verbosity2("cleanup {}".format(tox_env.package)) tox_env.package.remove() py.path.local(tox_env.package.dirname).remove(ignore_errors=True) def _copyfiles(self, srcdir, pathlist, destdir): for relpath in pathlist: src = srcdir.join(relpath) if not src.check(): self.report.error("missing source file: {}".format(src)) raise SystemExit(1) target = destdir.join(relpath) target.dirpath().ensure(dir=1) src.copy(target) def make_emptydir(self, path): if path.check(): self.report.info(" removing {}".format(path)) shutil.rmtree(str(path), ignore_errors=True) path.ensure(dir=1) def setupenv(self, venv): if venv.envconfig.missing_subs: venv.status = ( "unresolvable substitution(s): {}. " "Environment variables are missing or defined recursively.".format( ",".join(["'{}'".format(m) for m in venv.envconfig.missing_subs]) ) ) return if not venv.matching_platform(): venv.status = "platform mismatch" return # we simply omit non-matching platforms with self.newaction(venv, "getenv", venv.envconfig.envdir) as action: venv.status = 0 default_ret_code = 1 envlog = self.resultlog.get_envlog(venv.name) try: status = venv.update(action=action) except IOError as e: if e.args[0] != 2: raise status = ( "Error creating virtualenv. Note that spaces in paths are " "not supported by virtualenv. Error details: {!r}".format(e) ) except tox.exception.InvocationError as e: status = ( "Error creating virtualenv. Note that some special characters (e.g. ':' and " "unicode symbols) in paths are not supported by virtualenv. Error details: " "{!r}".format(e) ) except tox.exception.InterpreterNotFound as e: status = e if self.config.option.skip_missing_interpreters == "true": default_ret_code = 0 if status: str_status = str(status) commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str_status, default_ret_code) venv.status = status if default_ret_code == 0: self.report.skip(str_status) else: self.report.error(str_status) return False commandpath = venv.getcommandpath("python") envlog.set_python_info(commandpath) return True def finishvenv(self, venv): with self.newaction(venv, "finishvenv"): venv.finish() return True def developpkg(self, venv, setupdir): with self.newaction(venv, "developpkg", setupdir) as action: try: venv.developpkg(setupdir, action) return True except tox.exception.InvocationError as exception: venv.status = exception return False def installpkg(self, venv, path): """Install package in the specified virtual environment. :param VenvConfig venv: Destination environment :param str path: Path to the distribution package. :return: True if package installed otherwise False. :rtype: bool """ self.resultlog.set_header(installpkg=py.path.local(path)) with self.newaction(venv, "installpkg", path) as action: try: venv.installpkg(path, action) return True except tox.exception.InvocationError as exception: venv.status = exception return False def subcommand_test(self): if self.config.skipsdist: self.report.info("skipping sdist step") else: for venv in self.venvlist: if not venv.envconfig.skip_install: venv.package = self.hook.tox_package(session=self, venv=venv) if not venv.package: return 2 if self.config.option.sdistonly: return for venv in self.venvlist: if self.setupenv(venv): if venv.envconfig.skip_install: self.finishvenv(venv) else: if venv.envconfig.usedevelop: self.developpkg(venv, self.config.setupdir) elif self.config.skipsdist: self.finishvenv(venv) else: self.installpkg(venv, venv.package) self.runenvreport(venv) self.runtestenv(venv) retcode = self._summary() return retcode def runenvreport(self, venv): """ Run an environment report to show which package versions are installed in the venv """ with self.newaction(venv, "envreport") as action: packages = self.hook.tox_runenvreport(venv=venv, action=action) action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) envlog.set_installed(packages) def runtestenv(self, venv, redirect=False): if venv.status == 0 and self.config.option.notest: venv.status = "skipped tests" else: if venv.status: return self.hook.tox_runtest_pre(venv=venv) if venv.status == 0: self.hook.tox_runtest(venv=venv, redirect=redirect) self.hook.tox_runtest_post(venv=venv) def _summary(self): self.report.startsummary() retcode = 0 for venv in self.venvlist: status = venv.status if isinstance(status, tox.exception.InterpreterNotFound): msg = " {}: {}".format(venv.envconfig.envname, str(status)) if self.config.option.skip_missing_interpreters == "true": self.report.skip(msg) else: retcode = 1 self.report.error(msg) elif status == "platform mismatch": msg = " {}: {}".format(venv.envconfig.envname, str(status)) self.report.skip(msg) elif status and status == "ignored failed command": msg = " {}: {}".format(venv.envconfig.envname, str(status)) self.report.good(msg) elif status and status != "skipped tests": msg = " {}: {}".format(venv.envconfig.envname, str(status)) self.report.error(msg) retcode = 1 else: if not status: status = "commands succeeded" self.report.good(" {}: {}".format(venv.envconfig.envname, status)) if not retcode: self.report.good(" congratulations :)") path = self.config.option.resultjson if path: path = py.path.local(path) path.write(self.resultlog.dumps_json()) self.report.line("wrote json report at: {}".format(path)) return retcode def showconfig(self): self.info_versions() self.report.keyvalue("config-file:", self.config.option.configfile) self.report.keyvalue("toxinipath: ", self.config.toxinipath) self.report.keyvalue("toxinidir: ", self.config.toxinidir) self.report.keyvalue("toxworkdir: ", self.config.toxworkdir) self.report.keyvalue("setupdir: ", self.config.setupdir) self.report.keyvalue("distshare: ", self.config.distshare) self.report.keyvalue("skipsdist: ", self.config.skipsdist) self.report.tw.line() for envconfig in self.config.envconfigs.values(): self.report.line("[testenv:{}]".format(envconfig.envname), bold=True) for attr in self.config._parser._testenv_attr: self.report.line(" {:<15} = {}".format(attr.name, getattr(envconfig, attr.name))) def showenvs(self, all_envs=False, description=False): env_conf = self.config.envconfigs # this contains all environments default = self.config.envlist # this only the defaults ignore = {self.config.isolated_build_env}.union(default) extra = [e for e in env_conf if e not in ignore] if all_envs else [] if description: self.report.line("default environments:") max_length = max(len(env) for env in (default + extra)) def report_env(e): if description: text = env_conf[e].description or "[no description]" msg = "{} -> {}".format(e.ljust(max_length), text).strip() else: msg = e self.report.line(msg) for e in default: report_env(e) if all_envs and extra: if description: self.report.line("") self.report.line("additional environments:") for e in extra: report_env(e) def info_versions(self): versions = ["tox-{}".format(tox.__version__)] proc = subprocess.Popen( (sys.executable, "-m", "virtualenv", "--version"), stdout=subprocess.PIPE ) out, _ = proc.communicate() versions.append("virtualenv-{}".format(out.decode("UTF-8").strip())) self.report.keyvalue("tool-versions:", " ".join(versions)) def _resolve_package(self, package_spec): try: return self._spec2pkg[package_spec] except KeyError: self._spec2pkg[package_spec] = x = self._get_latest_version_of_package(package_spec) return x def _get_latest_version_of_package(self, package_spec): if not os.path.isabs(str(package_spec)): return package_spec p = py.path.local(package_spec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) self.report.info("determining {}".format(p)) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox.exception.MissingDependency(package_spec) if len(candidates) > 1: version_package = [] for filename in candidates: version = get_version_from_filename(filename.basename) if version is not None: version_package.append((version, filename)) else: self.report.warning("could not determine version of: {}".format(str(filename))) if not version_package: raise tox.exception.MissingDependency(package_spec) version_package.sort() _, package_with_largest_version = version_package[-1] return package_with_largest_version else: return candidates[0]
class Session: """ (unstable API). the session object that ties together configuration, reporting, venv creation, testing. """ def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) # self.report.using("logdir %s" %(self.config.logdir,)) self.report.using("tox.ini: %s" % (self.config.toxinipath,)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [ self.getvenv(x) for x in self.config.envlist ] except LookupError: raise SystemExit(1) except tox.exception.ConfigError as e: self.report.error(str(e)) raise SystemExit(1) self._actions = [] @property def hook(self): return self.config.pluginmanager.hook def _makevenv(self, name): envconfig = self.config.envconfigs.get(name, None) if envconfig is None: self.report.error("unknown environment %r" % name) raise LookupError(name) elif envconfig.envdir == self.config.toxinidir: self.report.error( "venv %r in %s would delete project" % (name, envconfig.envdir)) raise tox.exception.ConfigError('envdir must not equal toxinidir') venv = VirtualEnv(envconfig=envconfig, session=self) self._name2venv[name] = venv return venv def getvenv(self, name): """ return a VirtualEnv controler object for the 'name' env. """ try: return self._name2venv[name] except KeyError: return self._makevenv(name) def newaction(self, venv, msg, *args): action = Action(self, venv, msg, args) self._actions.append(action) return action def runcommand(self): self.report.using("tox-%s from %s" % (tox.__version__, tox.__file__)) if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs() else: return self.subcommand_test() def _copyfiles(self, srcdir, pathlist, destdir): for relpath in pathlist: src = srcdir.join(relpath) if not src.check(): self.report.error("missing source file: %s" % (src,)) raise SystemExit(1) target = destdir.join(relpath) target.dirpath().ensure(dir=1) src.copy(target) def _makesdist(self): setup = self.config.setupdir.join("setup.py") if not setup.check(): raise tox.exception.MissingFile(setup) action = self.newaction(None, "packaging") with action: action.setactivity("sdist-make", setup) self.make_emptydir(self.config.distdir) action.popen([sys.executable, setup, "sdist", "--formats=zip", "--dist-dir", self.config.distdir, ], cwd=self.config.setupdir) try: return self.config.distdir.listdir()[0] except py.error.ENOENT: # check if empty or comment only data = [] with open(str(setup)) as fp: for line in fp: if line and line[0] == '#': continue data.append(line) if not ''.join(data).strip(): self.report.error( 'setup.py is empty' ) raise SystemExit(1) self.report.error( 'No dist directory found. Please check setup.py, e.g with:\n' ' python setup.py sdist' ) raise SystemExit(1) def make_emptydir(self, path): if path.check(): self.report.info(" removing %s" % path) py.std.shutil.rmtree(str(path), ignore_errors=True) path.ensure(dir=1) def setupenv(self, venv): if not venv.matching_platform(): venv.status = "platform mismatch" return # we simply omit non-matching platforms action = self.newaction(venv, "getenv", venv.envconfig.envdir) with action: venv.status = 0 envlog = self.resultlog.get_envlog(venv.name) try: status = venv.update(action=action) except tox.exception.InvocationError: status = sys.exc_info()[1] if status: commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str(status), 1) venv.status = status self.report.error(str(status)) return False commandpath = venv.getcommandpath("python") envlog.set_python_info(commandpath) return True def finishvenv(self, venv): action = self.newaction(venv, "finishvenv") with action: venv.finish() return True def developpkg(self, venv, setupdir): action = self.newaction(venv, "developpkg", setupdir) with action: try: venv.developpkg(setupdir, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def installpkg(self, venv, path): """Install package in the specified virtual environment. :param :class:`tox.config.VenvConfig`: Destination environment :param str path: Path to the distribution package. :return: True if package installed otherwise False. :rtype: bool """ self.resultlog.set_header(installpkg=py.path.local(path)) action = self.newaction(venv, "installpkg", path) with action: try: venv.installpkg(path, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def get_installpkg_path(self): """ :return: Path to the distribution :rtype: py.path.local """ if not self.config.option.sdistonly and (self.config.sdistsrc or self.config.option.installpkg): path = self.config.option.installpkg if not path: path = self.config.sdistsrc path = self._resolve_pkg(path) self.report.info("using package %r, skipping 'sdist' activity " % str(path)) else: try: path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] self.report.error("FAIL could not package project - v = %r" % v) return sdistfile = self.config.distshare.join(path.basename) if sdistfile != path: self.report.info("copying new sdistfile to %r" % str(sdistfile)) try: sdistfile.dirpath().ensure(dir=1) except py.error.Error: self.report.warning("could not copy distfile to %s" % sdistfile.dirpath()) else: path.copy(sdistfile) return path def subcommand_test(self): if self.config.skipsdist: self.report.info("skipping sdist step") path = None else: path = self.get_installpkg_path() if not path: return 2 if self.config.option.sdistonly: return for venv in self.venvlist: if self.setupenv(venv): if venv.envconfig.usedevelop: self.developpkg(venv, self.config.setupdir) elif self.config.skipsdist or venv.envconfig.skip_install: self.finishvenv(venv) else: self.installpkg(venv, path) # write out version dependency information action = self.newaction(venv, "envreport") with action: args = venv.envconfig.list_dependencies_command output = venv._pcall(args, cwd=self.config.toxinidir, action=action) # the output contains a mime-header, skip it output = output.split("\n\n")[-1] packages = output.strip().split("\n") action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) envlog.set_installed(packages) self.runtestenv(venv) retcode = self._summary() return retcode def runtestenv(self, venv, redirect=False): if not self.config.option.notest: if venv.status: return self.hook.tox_runtest_pre(venv=venv) venv.test(redirect=redirect) self.hook.tox_runtest_post(venv=venv) else: venv.status = "skipped tests" def _summary(self): self.report.startsummary() retcode = 0 for venv in self.venvlist: status = venv.status if isinstance(status, tox.exception.InterpreterNotFound): msg = " %s: %s" % (venv.envconfig.envname, str(status)) if self.config.option.skip_missing_interpreters: self.report.skip(msg) else: retcode = 1 self.report.error(msg) elif status == "platform mismatch": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.skip(msg) elif status and status == "ignored failed command": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.good(msg) elif status and status != "skipped tests": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.error(msg) retcode = 1 else: if not status: status = "commands succeeded" self.report.good(" %s: %s" % (venv.envconfig.envname, status)) if not retcode: self.report.good(" congratulations :)") path = self.config.option.resultjson if path: path = py.path.local(path) path.write(self.resultlog.dumps_json()) self.report.line("wrote json report at: %s" % path) return retcode def showconfig(self): self.info_versions() self.report.keyvalue("config-file:", self.config.option.configfile) self.report.keyvalue("toxinipath: ", self.config.toxinipath) self.report.keyvalue("toxinidir: ", self.config.toxinidir) self.report.keyvalue("toxworkdir: ", self.config.toxworkdir) self.report.keyvalue("setupdir: ", self.config.setupdir) self.report.keyvalue("distshare: ", self.config.distshare) self.report.keyvalue("skipsdist: ", self.config.skipsdist) self.report.tw.line() for envconfig in self.config.envconfigs.values(): self.report.line("[testenv:%s]" % envconfig.envname, bold=True) for attr in self.config._parser._testenv_attr: self.report.line(" %-15s = %s" % (attr.name, getattr(envconfig, attr.name))) def showenvs(self): for env in self.config.envlist: self.report.line("%s" % env) def info_versions(self): versions = ['tox-%s' % tox.__version__] try: version = py.process.cmdexec("virtualenv --version") except py.process.cmdexec.Error: versions.append("virtualenv-1.9.1 (vendored)") else: versions.append("virtualenv-%s" % version.strip()) self.report.keyvalue("tool-versions:", " ".join(versions)) def _resolve_pkg(self, pkgspec): try: return self._spec2pkg[pkgspec] except KeyError: self._spec2pkg[pkgspec] = x = self._resolvepkg(pkgspec) return x def _resolvepkg(self, pkgspec): if not os.path.isabs(str(pkgspec)): return pkgspec p = py.path.local(pkgspec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) self.report.info("determining %s" % p) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox.exception.MissingDependency(pkgspec) if len(candidates) > 1: items = [] for x in candidates: ver = getversion(x.basename) if ver is not None: items.append((ver, x)) else: self.report.warning("could not determine version of: %s" % str(x)) items.sort() if not items: raise tox.exception.MissingDependency(pkgspec) return items[-1][1] else: return candidates[0]
class Session: """ (unstable API). the session object that ties together configuration, reporting, venv creation, testing. """ def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) # self.report.using("logdir %s" %(self.config.logdir,)) self.report.using("tox.ini: %s" % (self.config.toxinipath,)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [ self.getvenv(x) for x in self.config.envlist ] except LookupError: raise SystemExit(1) except tox.exception.ConfigError as e: self.report.error(str(e)) raise SystemExit(1) self._actions = [] @property def hook(self): return self.config.pluginmanager.hook def _makevenv(self, name): envconfig = self.config.envconfigs.get(name, None) if envconfig is None: self.report.error("unknown environment %r" % name) raise LookupError(name) elif envconfig.envdir == self.config.toxinidir: self.report.error( "venv %r in %s would delete project" % (name, envconfig.envdir)) raise tox.exception.ConfigError('envdir must not equal toxinidir') venv = VirtualEnv(envconfig=envconfig, session=self) self._name2venv[name] = venv return venv def getvenv(self, name): """ return a VirtualEnv controler object for the 'name' env. """ try: return self._name2venv[name] except KeyError: return self._makevenv(name) def newaction(self, venv, msg, *args): action = Action(self, venv, msg, args) self._actions.append(action) return action def runcommand(self): self.report.using("tox-%s from %s" % (tox.__version__, tox.__file__)) if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs(all_envs=False, description=self.config.option.verbosity > 0) elif self.config.option.listenvs_all: self.showenvs(all_envs=True, description=self.config.option.verbosity > 0) else: return self.subcommand_test() def _copyfiles(self, srcdir, pathlist, destdir): for relpath in pathlist: src = srcdir.join(relpath) if not src.check(): self.report.error("missing source file: %s" % (src,)) raise SystemExit(1) target = destdir.join(relpath) target.dirpath().ensure(dir=1) src.copy(target) def _makesdist(self): setup = self.config.setupdir.join("setup.py") if not setup.check(): raise tox.exception.MissingFile(setup) action = self.newaction(None, "packaging") with action: action.setactivity("sdist-make", setup) self.make_emptydir(self.config.distdir) action.popen([sys.executable, setup, "sdist", "--formats=zip", "--dist-dir", self.config.distdir, ], cwd=self.config.setupdir) try: return self.config.distdir.listdir()[0] except py.error.ENOENT: # check if empty or comment only data = [] with open(str(setup)) as fp: for line in fp: if line and line[0] == '#': continue data.append(line) if not ''.join(data).strip(): self.report.error( 'setup.py is empty' ) raise SystemExit(1) self.report.error( 'No dist directory found. Please check setup.py, e.g with:\n' ' python setup.py sdist' ) raise SystemExit(1) def make_emptydir(self, path): if path.check(): self.report.info(" removing %s" % path) shutil.rmtree(str(path), ignore_errors=True) path.ensure(dir=1) def setupenv(self, venv): if venv.envconfig.missing_subs: venv.status = ( "unresolvable substitution(s): %s. " "Environment variables are missing or defined recursively." % (','.join(["'%s'" % m for m in venv.envconfig.missing_subs]))) return if not venv.matching_platform(): venv.status = "platform mismatch" return # we simply omit non-matching platforms action = self.newaction(venv, "getenv", venv.envconfig.envdir) with action: venv.status = 0 envlog = self.resultlog.get_envlog(venv.name) try: status = venv.update(action=action) except IOError as e: if e.args[0] != 2: raise status = ( "Error creating virtualenv. Note that spaces in paths are " "not supported by virtualenv. Error details: %r" % e) except tox.exception.InvocationError as e: status = ( "Error creating virtualenv. Note that some special " "characters (e.g. ':' and unicode symbols) in paths are " "not supported by virtualenv. Error details: %r" % e) if status: commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str(status), 1) venv.status = status self.report.error(str(status)) return False commandpath = venv.getcommandpath("python") envlog.set_python_info(commandpath) return True def finishvenv(self, venv): action = self.newaction(venv, "finishvenv") with action: venv.finish() return True def developpkg(self, venv, setupdir): action = self.newaction(venv, "developpkg", setupdir) with action: try: venv.developpkg(setupdir, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def installpkg(self, venv, path): """Install package in the specified virtual environment. :param VenvConfig venv: Destination environment :param str path: Path to the distribution package. :return: True if package installed otherwise False. :rtype: bool """ self.resultlog.set_header(installpkg=py.path.local(path)) action = self.newaction(venv, "installpkg", path) with action: try: venv.installpkg(path, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def get_installpkg_path(self): """ :return: Path to the distribution :rtype: py.path.local """ if not self.config.option.sdistonly and (self.config.sdistsrc or self.config.option.installpkg): path = self.config.option.installpkg if not path: path = self.config.sdistsrc path = self._resolve_pkg(path) self.report.info("using package %r, skipping 'sdist' activity " % str(path)) else: try: path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] self.report.error("FAIL could not package project - v = %r" % v) return sdistfile = self.config.distshare.join(path.basename) if sdistfile != path: self.report.info("copying new sdistfile to %r" % str(sdistfile)) try: sdistfile.dirpath().ensure(dir=1) except py.error.Error: self.report.warning("could not copy distfile to %s" % sdistfile.dirpath()) else: path.copy(sdistfile) return path def subcommand_test(self): if self.config.skipsdist: self.report.info("skipping sdist step") path = None else: path = self.get_installpkg_path() if not path: return 2 if self.config.option.sdistonly: return for venv in self.venvlist: if self.setupenv(venv): if venv.envconfig.skip_install: self.finishvenv(venv) else: if venv.envconfig.usedevelop: self.developpkg(venv, self.config.setupdir) elif self.config.skipsdist: self.finishvenv(venv) else: self.installpkg(venv, path) # write out version dependency information action = self.newaction(venv, "envreport") with action: args = venv.envconfig.list_dependencies_command output = venv._pcall(args, cwd=self.config.toxinidir, action=action) # the output contains a mime-header, skip it output = output.split("\n\n")[-1] packages = output.strip().split("\n") action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) envlog.set_installed(packages) self.runtestenv(venv) retcode = self._summary() return retcode def runtestenv(self, venv, redirect=False): if not self.config.option.notest: if venv.status: return self.hook.tox_runtest_pre(venv=venv) self.hook.tox_runtest(venv=venv, redirect=redirect) self.hook.tox_runtest_post(venv=venv) else: venv.status = "skipped tests" def _summary(self): self.report.startsummary() retcode = 0 for venv in self.venvlist: status = venv.status if isinstance(status, tox.exception.InterpreterNotFound): msg = " %s: %s" % (venv.envconfig.envname, str(status)) if self.config.option.skip_missing_interpreters: self.report.skip(msg) else: retcode = 1 self.report.error(msg) elif status == "platform mismatch": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.skip(msg) elif status and status == "ignored failed command": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.good(msg) elif status and status != "skipped tests": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.error(msg) retcode = 1 else: if not status: status = "commands succeeded" self.report.good(" %s: %s" % (venv.envconfig.envname, status)) if not retcode: self.report.good(" congratulations :)") path = self.config.option.resultjson if path: path = py.path.local(path) path.write(self.resultlog.dumps_json()) self.report.line("wrote json report at: %s" % path) return retcode def showconfig(self): self.info_versions() self.report.keyvalue("config-file:", self.config.option.configfile) self.report.keyvalue("toxinipath: ", self.config.toxinipath) self.report.keyvalue("toxinidir: ", self.config.toxinidir) self.report.keyvalue("toxworkdir: ", self.config.toxworkdir) self.report.keyvalue("setupdir: ", self.config.setupdir) self.report.keyvalue("distshare: ", self.config.distshare) self.report.keyvalue("skipsdist: ", self.config.skipsdist) self.report.tw.line() for envconfig in self.config.envconfigs.values(): self.report.line("[testenv:%s]" % envconfig.envname, bold=True) for attr in self.config._parser._testenv_attr: self.report.line(" %-15s = %s" % (attr.name, getattr(envconfig, attr.name))) def showenvs(self, all_envs=False, description=False): env_conf = self.config.envconfigs # this contains all environments default = self.config.envlist # this only the defaults extra = sorted([e for e in env_conf if e not in default]) if all_envs else [] if description: self.report.line('default environments:') max_length = max(len(env) for env in (default + extra)) def report_env(e): if description: text = env_conf[e].description or '[no description]' msg = '{0} -> {1}'.format(e.ljust(max_length), text).strip() else: msg = e self.report.line(msg) for e in default: report_env(e) if all_envs and extra: if description: self.report.line('') self.report.line('additional environments:') for e in extra: report_env(e) def info_versions(self): versions = ['tox-%s' % tox.__version__] proc = subprocess.Popen( (sys.executable, '-m', 'virtualenv', '--version'), stdout=subprocess.PIPE, ) out, _ = proc.communicate() versions.append('virtualenv-{0}'.format(out.decode('UTF-8').strip())) self.report.keyvalue("tool-versions:", " ".join(versions)) def _resolve_pkg(self, pkgspec): try: return self._spec2pkg[pkgspec] except KeyError: self._spec2pkg[pkgspec] = x = self._resolvepkg(pkgspec) return x def _resolvepkg(self, pkgspec): if not os.path.isabs(str(pkgspec)): return pkgspec p = py.path.local(pkgspec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) self.report.info("determining %s" % p) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox.exception.MissingDependency(pkgspec) if len(candidates) > 1: items = [] for x in candidates: ver = getversion(x.basename) if ver is not None: items.append((ver, x)) else: self.report.warning("could not determine version of: %s" % str(x)) items.sort() if not items: raise tox.exception.MissingDependency(pkgspec) return items[-1][1] else: return candidates[0]
class Session: def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) #self.report.using("logdir %s" %(self.config.logdir,)) self.report.using("tox.ini: %s" % (self.config.toxinipath, )) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [self.getvenv(x) for x in self.config.envlist] except LookupError: raise SystemExit(1) self._actions = [] def _makevenv(self, name): envconfig = self.config.envconfigs.get(name, None) if envconfig is None: self.report.error("unknown environment %r" % name) raise LookupError(name) venv = VirtualEnv(envconfig=envconfig, session=self) self._name2venv[name] = venv return venv def getvenv(self, name): """ return a VirtualEnv controler object for the 'name' env. """ try: return self._name2venv[name] except KeyError: return self._makevenv(name) def newaction(self, venv, msg, *args): action = Action(self, venv, msg, args) self._actions.append(action) return action def runcommand(self): self.report.using("tox-%s from %s" % (tox.__version__, tox.__file__)) if self.config.minversion: minversion = NormalizedVersion(self.config.minversion) toxversion = NormalizedVersion(tox.__version__) if toxversion < minversion: self.report.error( "tox version is %s, required is at least %s" % (toxversion, minversion)) raise SystemExit(1) if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs() else: return self.subcommand_test() def _copyfiles(self, srcdir, pathlist, destdir): for relpath in pathlist: src = srcdir.join(relpath) if not src.check(): self.report.error("missing source file: %s" % (src, )) raise SystemExit(1) target = destdir.join(relpath) target.dirpath().ensure(dir=1) src.copy(target) def _makesdist(self): setup = self.config.setupdir.join("setup.py") if not setup.check(): raise tox.exception.MissingFile(setup) action = self.newaction(None, "packaging") with action: action.setactivity("sdist-make", setup) self.make_emptydir(self.config.distdir) action.popen([ sys.executable, setup, "sdist", "--formats=zip", "--dist-dir", self.config.distdir, ], cwd=self.config.setupdir) try: return self.config.distdir.listdir()[0] except py.error.ENOENT: # check if empty or comment only data = [] with open(str(setup)) as fp: for line in fp: if line and line[0] == '#': continue data.append(line) if not ''.join(data).strip(): self.report.error('setup.py is empty') raise SystemExit(1) self.report.error( 'No dist directory found. Please check setup.py, e.g with:\n'\ ' python setup.py sdist' ) raise SystemExit(1) def make_emptydir(self, path): if path.check(): self.report.info(" removing %s" % path) py.std.shutil.rmtree(str(path), ignore_errors=True) path.ensure(dir=1) def setupenv(self, venv): action = self.newaction(venv, "getenv", venv.envconfig.envdir) with action: venv.status = 0 envlog = self.resultlog.get_envlog(venv.name) try: status = venv.update(action=action) except tox.exception.InvocationError: status = sys.exc_info()[1] if status: commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str(status), 1) venv.status = status self.report.error(str(status)) return False commandpath = venv.getcommandpath("python") envlog.set_python_info(commandpath) return True def finishvenv(self, venv): action = self.newaction(venv, "finishvenv") with action: venv.finish() return True def developpkg(self, venv, setupdir): action = self.newaction(venv, "developpkg", setupdir) with action: try: venv.developpkg(setupdir, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def installpkg(self, venv, sdist_path): self.resultlog.set_header(installpkg=sdist_path) action = self.newaction(venv, "installpkg", sdist_path) with action: try: venv.installpkg(sdist_path, action) return True except tox.exception.InvocationError: venv.status = sys.exc_info()[1] return False def sdist(self): if not self.config.option.sdistonly and ( self.config.sdistsrc or self.config.option.installpkg): sdist_path = self.config.option.installpkg if not sdist_path: sdist_path = self.config.sdistsrc sdist_path = self._resolve_pkg(sdist_path) self.report.info("using package %r, skipping 'sdist' activity " % str(sdist_path)) else: try: sdist_path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] self.report.error("FAIL could not package project") return sdistfile = self.config.distshare.join(sdist_path.basename) if sdistfile != sdist_path: self.report.info("copying new sdistfile to %r" % str(sdistfile)) try: sdistfile.dirpath().ensure(dir=1) except py.error.Error: self.report.warning("could not copy distfile to %s" % sdistfile.dirpath()) else: sdist_path.copy(sdistfile) return sdist_path def subcommand_test(self): if self.config.skipsdist: self.report.info("skipping sdist step") sdist_path = None else: sdist_path = self.sdist() if not sdist_path: return 2 if self.config.option.sdistonly: return for venv in self.venvlist: if self.setupenv(venv): if venv.envconfig.develop: self.developpkg(venv, self.config.setupdir) elif self.config.skipsdist: self.finishvenv(venv) else: self.installpkg(venv, sdist_path) self.runtestenv(venv) retcode = self._summary() return retcode def runtestenv(self, venv, redirect=False): if not self.config.option.notest: if venv.status: return venv.test(redirect=redirect) else: venv.status = "skipped tests" def _summary(self): self.report.startsummary() retcode = 0 for venv in self.venvlist: status = venv.status if status and status != "skipped tests": msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.error(msg) retcode = 1 else: if not status: status = "commands succeeded" self.report.good(" %s: %s" % (venv.envconfig.envname, status)) if not retcode: self.report.good(" congratulations :)") path = self.config.option.resultjson if path: path = py.path.local(path) path.write(self.resultlog.dumps_json()) self.report.line("wrote json report at: %s" % path) return retcode def showconfig(self): self.info_versions() self.report.keyvalue("config-file:", self.config.option.configfile) self.report.keyvalue("toxinipath: ", self.config.toxinipath) self.report.keyvalue("toxinidir: ", self.config.toxinidir) self.report.keyvalue("toxworkdir: ", self.config.toxworkdir) self.report.keyvalue("setupdir: ", self.config.setupdir) self.report.keyvalue("distshare: ", self.config.distshare) self.report.keyvalue("skipsdist: ", self.config.skipsdist) self.report.tw.line() for envconfig in self.config.envconfigs.values(): self.report.line("[testenv:%s]" % envconfig.envname, bold=True) self.report.line(" basepython=%s" % envconfig.basepython) self.report.line(" _basepython_info=%s" % envconfig._basepython_info) self.report.line(" envpython=%s" % envconfig.envpython) self.report.line(" envtmpdir=%s" % envconfig.envtmpdir) self.report.line(" envbindir=%s" % envconfig.envbindir) self.report.line(" envlogdir=%s" % envconfig.envlogdir) self.report.line(" changedir=%s" % envconfig.changedir) self.report.line(" args_are_path=%s" % envconfig.args_are_paths) self.report.line(" install_command=%s" % envconfig.install_command) self.report.line(" commands=") for command in envconfig.commands: self.report.line(" %s" % command) self.report.line(" deps=%s" % envconfig.deps) self.report.line(" envdir= %s" % envconfig.envdir) self.report.line(" downloadcache=%s" % envconfig.downloadcache) self.report.line(" usedevelop=%s" % envconfig.develop) def showenvs(self): for env in self.config.envlist: self.report.line("%s" % env) def info_versions(self): versions = ['tox-%s' % tox.__version__] try: version = py.process.cmdexec("virtualenv --version") except py.process.cmdexec.Error: versions.append("virtualenv-1.9.1 (vendored)") else: versions.append("virtualenv-%s" % version.strip()) self.report.keyvalue("tool-versions:", " ".join(versions)) def _resolve_pkg(self, pkgspec): try: return self._spec2pkg[pkgspec] except KeyError: self._spec2pkg[pkgspec] = x = self._resolvepkg(pkgspec) return x def _resolvepkg(self, pkgspec): if not os.path.isabs(str(pkgspec)): return pkgspec p = py.path.local(pkgspec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) self.report.info("determining %s" % p) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox.exception.MissingDependency(pkgspec) if len(candidates) > 1: items = [] for x in candidates: ver = getversion(x.basename) if ver is not None: items.append((ver, x)) else: self.report.warning("could not determine version of: %s" % str(x)) items.sort() if not items: raise tox.exception.MissingDependency(pkgspec) return items[-1][1] else: return candidates[0]
def __init__(self): self._clearmocks() self.config = request.getfixturevalue("newconfig")([], "") self.resultlog = ResultLog() self._actions = []
class Session: """The session object that ties together configuration, reporting, venv creation, testing.""" def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config self.popen = popen self.resultlog = ResultLog() self.report = Report(self) self.make_emptydir(config.logdir) config.logdir.ensure(dir=1) self.report.using("tox.ini: {}".format(self.config.toxinipath)) self._spec2pkg = {} self._name2venv = {} try: self.venvlist = [ self.getvenv(x) for x in self.evaluated_env_list() ] except LookupError: raise SystemExit(1) except tox.exception.ConfigError as exception: self.report.error(str(exception)) raise SystemExit(1) try: self.venv_order = stable_topological_sort( OrderedDict( (v.name, v.envconfig.depends) for v in self.venvlist)) except ValueError as exception: self.report.error( "circular dependency detected: {}".format(exception)) raise SystemExit(1) self._actions = [] 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 for name in self.config.envlist: 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) self.report.verbosity1(msg) continue yield name @property def hook(self): return self.config.pluginmanager.hook def _makevenv(self, name): envconfig = self.config.envconfigs.get(name, None) if envconfig is None: self.report.error("unknown environment {!r}".format(name)) raise LookupError(name) elif envconfig.envdir == self.config.toxinidir: self.report.error("venv {!r} in {} would delete project".format( name, envconfig.envdir)) raise tox.exception.ConfigError("envdir must not equal toxinidir") venv = VirtualEnv(envconfig=envconfig, session=self) self._name2venv[name] = venv return venv def getvenv(self, name): """ return a VirtualEnv controler object for the 'name' env. """ try: return self._name2venv[name] except KeyError: return self._makevenv(name) def newaction(self, venv, msg, *args): action = Action(self, venv, msg, args) self._actions.append(action) return action def runcommand(self): self.report.using("tox-{} from {}".format(tox.__version__, tox.__file__)) verbosity = self.report.verbosity > Verbosity.DEFAULT if self.config.option.showconfig: self.showconfig() elif self.config.option.listenvs: self.showenvs(all_envs=False, description=verbosity) elif self.config.option.listenvs_all: self.showenvs(all_envs=True, description=verbosity) else: with self.cleanup(): return self.subcommand_test() @contextmanager def cleanup(self): self.config.temp_dir.ensure(dir=True) try: yield finally: for name in self.venv_order: tox_env = self.getvenv(name) if (hasattr(tox_env, "package") and isinstance(tox_env.package, py.path.local) and tox_env.package.exists()): self.report.verbosity2("cleanup {}".format( tox_env.package)) tox_env.package.remove() py.path.local( tox_env.package.dirname).remove(ignore_errors=True) def make_emptydir(self, path): if path.check(): self.report.info(" removing {}".format(path)) shutil.rmtree(str(path), ignore_errors=True) path.ensure(dir=1) def setupenv(self, venv): if venv.envconfig.missing_subs: venv.status = ( "unresolvable substitution(s): {}. " "Environment variables are missing or defined recursively.". format(",".join( ["'{}'".format(m) for m in venv.envconfig.missing_subs]))) return if not venv.matching_platform(): venv.status = "platform mismatch" return # we simply omit non-matching platforms with self.newaction(venv, "getenv", venv.envconfig.envdir) as action: venv.status = 0 default_ret_code = 1 envlog = self.resultlog.get_envlog(venv.name) try: status = venv.update(action=action) except IOError as e: if e.args[0] != 2: raise status = ( "Error creating virtualenv. Note that spaces in paths are " "not supported by virtualenv. Error details: {!r}".format( e)) except tox.exception.InvocationError as e: status = ( "Error creating virtualenv. Note that some special characters (e.g. ':' and " "unicode symbols) in paths are not supported by virtualenv. Error details: " "{!r}".format(e)) except tox.exception.InterpreterNotFound as e: status = e if self.config.option.skip_missing_interpreters == "true": default_ret_code = 0 if status: str_status = str(status) commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str_status, default_ret_code) venv.status = status if default_ret_code == 0: self.report.skip(str_status) else: self.report.error(str_status) return False commandpath = venv.getcommandpath("python") envlog.set_python_info(commandpath) return True def finishvenv(self, venv): with self.newaction(venv, "finishvenv"): venv.finish() return True def developpkg(self, venv, setupdir): with self.newaction(venv, "developpkg", setupdir) as action: try: venv.developpkg(setupdir, action) return True except tox.exception.InvocationError as exception: venv.status = exception return False def installpkg(self, venv, path): """Install package in the specified virtual environment. :param VenvConfig venv: Destination environment :param str path: Path to the distribution package. :return: True if package installed otherwise False. :rtype: bool """ self.resultlog.set_header(installpkg=py.path.local(path)) with self.newaction(venv, "installpkg", path) as action: try: venv.installpkg(path, action) return True except tox.exception.InvocationError as exception: venv.status = exception return False def subcommand_test(self): if self.config.skipsdist: self.report.info("skipping sdist step") else: for name in self.venv_order: venv = self.getvenv(name) 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 in os.environ if not within_parallel and self.config.option.parallel != PARALLEL_OFF: self.run_parallel() else: self.run_sequential() retcode = self._summary() return retcode def run_sequential(self): for name in self.venv_order: venv = self.getvenv(name) if self.setupenv(venv): if venv.envconfig.skip_install: self.finishvenv(venv) else: if venv.envconfig.usedevelop: self.developpkg(venv, self.config.setupdir) elif self.config.skipsdist: self.finishvenv(venv) else: self.installpkg(venv, venv.package) self.runenvreport(venv) self.runtestenv(venv) def run_parallel(self): """here we'll just start parallel sub-processes""" live_out = self.config.option.parallel_live args = [sys.executable, "-m", "tox"] + self.config.args try: position = args.index("--") except ValueError: position = len(args) try: parallel_at = args[0:position].index("--parallel") del args[parallel_at] position -= 1 except ValueError: pass max_parallel = self.config.option.parallel if max_parallel is None: max_parallel = len(self.venv_order) semaphore = Semaphore(max_parallel) finished = Event() sink = None if live_out else subprocess.PIPE show_progress = not live_out and self.report.verbosity > Verbosity.QUIET with Spinner(enabled=show_progress) as spinner: 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 self.config.option.notest else ("parallel child exit code {}".format(res) if res else res)) done.add(env_name) report = spinner.succeed if self.config.option.notest: report = spinner.skip elif res: report = spinner.fail report(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() self.report.logline_if(Verbosity.QUIET, message) threads = [] todo_keys = set(self.venv_order) todo = OrderedDict( (i, todo_keys & set(self.getvenv(i).envconfig.depends)) for i in self.venv_order) done = set() while todo: for name, depends in list(todo.items()): if depends - done: # skip if has unfinished dependencies continue del todo[name] venv = self.getvenv(name) semaphore.acquire(blocking=True) spinner.add(name) thread = Thread(target=run_in_thread, args=(venv, os.environ.copy())) thread.start() threads.append(thread) if todo: # wait until someone finishes and retry queuing jobs finished.wait() finished.clear() for thread in threads: thread.join() def runenvreport(self, venv): """ Run an environment report to show which package versions are installed in the venv """ with self.newaction(venv, "envreport") as action: packages = self.hook.tox_runenvreport(venv=venv, action=action) action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) envlog.set_installed(packages) def runtestenv(self, venv, redirect=False): if venv.status == 0 and self.config.option.notest: venv.status = "skipped tests" else: if venv.status: return self.hook.tox_runtest_pre(venv=venv) if venv.status == 0: self.hook.tox_runtest(venv=venv, redirect=redirect) self.hook.tox_runtest_post(venv=venv) def _summary(self): is_parallel_child = PARALLEL_ENV_VAR_KEY in os.environ if not is_parallel_child: self.report.startsummary() exit_code = 0 for name in self.venv_order: venv = self.getvenv(name) reporter = self.report.good status = venv.status if isinstance(status, tox.exception.InterpreterNotFound): msg = " {}: {}".format(venv.envconfig.envname, str(status)) if self.config.option.skip_missing_interpreters == "true": reporter = self.report.skip else: exit_code = 1 reporter = self.report.error elif status == "platform mismatch": msg = " {}: {}".format(venv.envconfig.envname, str(status)) reporter = self.report.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)) reporter = self.report.error exit_code = 1 else: if not status: status = "commands succeeded" msg = " {}: {}".format(venv.envconfig.envname, status) if not is_parallel_child: reporter(msg) if not exit_code and not is_parallel_child: self.report.good(" congratulations :)") if not is_parallel_child: path = self.config.option.resultjson if path: path = py.path.local(path) path.write(self.resultlog.dumps_json()) self.report.line("wrote json report at: {}".format(path)) return exit_code def showconfig(self): self.info_versions() self.report.keyvalue("config-file:", self.config.option.configfile) self.report.keyvalue("toxinipath: ", self.config.toxinipath) self.report.keyvalue("toxinidir: ", self.config.toxinidir) self.report.keyvalue("toxworkdir: ", self.config.toxworkdir) self.report.keyvalue("setupdir: ", self.config.setupdir) self.report.keyvalue("distshare: ", self.config.distshare) self.report.keyvalue("skipsdist: ", self.config.skipsdist) self.report.tw.line() for envconfig in self.config.envconfigs.values(): self.report.line("[testenv:{}]".format(envconfig.envname), bold=True) for attr in self.config._parser._testenv_attr: self.report.line(" {:<15} = {}".format( attr.name, getattr(envconfig, attr.name))) def showenvs(self, all_envs=False, description=False): env_conf = self.config.envconfigs # this contains all environments default = self.config.envlist # this only the defaults ignore = {self.config.isolated_build_env}.union(default) extra = [e for e in env_conf if e not in ignore] if all_envs else [] if description: self.report.line("default environments:") max_length = max(len(env) for env in (default + extra)) def report_env(e): if description: text = env_conf[e].description or "[no description]" msg = "{} -> {}".format(e.ljust(max_length), text).strip() else: msg = e self.report.line(msg) for e in default: report_env(e) if all_envs and extra: if description: self.report.line("") self.report.line("additional environments:") for e in extra: report_env(e) def info_versions(self): versions = ["tox-{}".format(tox.__version__)] proc = subprocess.Popen( (sys.executable, "-m", "virtualenv", "--version"), stdout=subprocess.PIPE) out, _ = proc.communicate() versions.append("virtualenv-{}".format(out.decode("UTF-8").strip())) self.report.keyvalue("tool-versions:", " ".join(versions)) def _resolve_package(self, package_spec): try: return self._spec2pkg[package_spec] except KeyError: self._spec2pkg[ package_spec] = x = self._get_latest_version_of_package( package_spec) return x def _get_latest_version_of_package(self, package_spec): if not os.path.isabs(str(package_spec)): return package_spec p = py.path.local(package_spec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) self.report.info("determining {}".format(p)) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox.exception.MissingDependency(package_spec) if len(candidates) > 1: version_package = [] for filename in candidates: version = get_version_from_filename(filename.basename) if version is not None: version_package.append((version, filename)) else: self.report.warning( "could not determine version of: {}".format( str(filename))) if not version_package: raise tox.exception.MissingDependency(package_spec) version_package.sort() _, package_with_largest_version = version_package[-1] return package_with_largest_version else: return candidates[0]