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_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
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_plus.__version__, tox_plus.__file__)) if self.config.minversion: minversion = NormalizedVersion(self.config.minversion) toxversion = NormalizedVersion(tox_plus.__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_plus.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_plus.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_plus.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_plus.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_plus.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: pip = venv.getcommandpath("pip") # we can't really call internal helpers here easily :/ # output = venv._pcall([str(pip), "freeze"], # cwd=self.config.toxinidir, # action=action) output = py.process.cmdexec("%s freeze" % (pip)) packages = output.strip().split("\n") action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) envlog.set_installed(packages) pm = get_plugin_manager() pm.hook.pretest(venv=venv) self.runtestenv(venv) pm.hook.posttest(venv=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 isinstance(status, tox_plus.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 != "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_plus.__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_plus.exception.MissingDirectory(p.dirpath()) self.report.info("determining %s" % p) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox_plus.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_plus.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_plus.__version__, tox_plus.__file__)) if self.config.minversion: minversion = NormalizedVersion(self.config.minversion) toxversion = NormalizedVersion(tox_plus.__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_plus.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_plus.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_plus.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_plus.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_plus.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: pip = venv.getcommandpath("pip") # we can't really call internal helpers here easily :/ # output = venv._pcall([str(pip), "freeze"], # cwd=self.config.toxinidir, # action=action) output = py.process.cmdexec("%s freeze" % (pip)) packages = output.strip().split("\n") action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) envlog.set_installed(packages) pm = get_plugin_manager() pm.hook.pretest(venv=venv) self.runtestenv(venv) pm.hook.posttest(venv=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 isinstance(status, tox_plus.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 != "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_plus.__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_plus.exception.MissingDirectory(p.dirpath()) self.report.info("determining %s" % p) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise tox_plus.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_plus.exception.MissingDependency(pkgspec) return items[-1][1] else: return candidates[0]