Commits

holger krekel  committed d04f6fe

streamline and somewhat simplify reporting, write release-announcement

  • Participants
  • Parent commits d11d6d9

Comments (0)

Files changed (6)

File doc/announce/release-1.4.txt

+tox 1.4: the virtualenv-based test run automatizer
+===========================================================================
+
+I am happy to announce tox 1.4 brings:
+
+- improvements with configuration file syntax, now allowing re-using
+  selected settings across config file sections. see 
+
+- terminal reporting was simplified and streamlined.  Now on
+  verbosity==0 (the default), less information will be shown
+  and you can use one or multiple "-v" options to increase verbosity.
+
+- internal re-organisation so that the separately released "detox" 
+  tool can reuse tox code to implement a fully distributed tox run. 
+
+More documentation:
+
+    http://tox.testrun.org/
+
+Installation:
+
+    pip install -U tox
+
+code hosting and issue tracking on bitbucket:
+
+    http://bitbucket.org/hpk42/tox
+
+What is tox?
+----------------
+
+tox standardizes and automates tedious test activities driven from a
+simple ``tox.ini`` file, including:
+
+* creation and management of different virtualenv environments 
+  with different Python interpreters
+* packaging and installing your package into each of them
+* running your test tool of choice, be it nose, py.test or unittest2 or other tools such as "sphinx" doc checks
+* testing dev packages against each other without needing to upload to PyPI
+
+best,
+Holger Krekel

File tests/test_venv.py

     venv = mocksession.getenv('python')
     l = mocksession._pcalls
     p = tmpdir.ensure("distfile.tar.gz")
-    venv.install_sdist(str(p))
+    mocksession.installsdist(venv, p)
     # two different index servers, two calls
     assert len(l) == 1
     args = " ".join(l[0].args)
     """)
     venv = mocksession.getenv('python')
     venv.update()
-    venv.install_sdist("xz")
-    mocksession.report.expect("*", "*no existing*")
+    mocksession.installsdist(venv, "xz")
+    mocksession.report.expect("verbosity0", "*create*")
     venv.update()
-    mocksession.report.expect("*", "*configchange*detected*")
+    mocksession.report.expect("verbosity0", "*recreate*")
 
 def test_install_error(newmocksession, monkeypatch):
     mocksession = newmocksession(['--recreate'], """
         cconfig = venv._getliveconfig()
         venv.update()
         assert not venv.path_config.check()
-        venv.install_sdist("sdist.zip")
+        mocksession.installsdist(venv, "sdist.zip")
         assert venv.path_config.check()
         assert mocksession._pcalls
         args1 = map(str, mocksession._pcalls[0].args)
         cconfig.python = py.path.local("balla")
         cconfig.writeconfig(venv.path_config)
         venv.update()
-        mocksession.report.expect("*", "configchange*")
+        mocksession.report.expect("verbosity0", "*recreate*")
 
     def test_dep_recreation(self, newconfig, mocksession):
         config = newconfig([], "")
 
     venv = VirtualEnv(config.envconfigs['python'], session=mocksession)
     # import pdb; pdb.set_trace()
-    venv.install_sdist("xyz")
+    mocksession.installsdist(venv, "xyz")
     venv.test()
 
     l = mocksession._pcalls
     venv = mocksession.getenv('python')
     venv.just_created = True
     venv.envconfig.envdir.ensure(dir=1)
-    venv.install_sdist("whatever")
+    mocksession.installsdist(venv, "whatever")
     l = mocksession._pcalls
     assert len(l) == 1
     assert '-U' not in l[0].args
     mocksession = newmocksession([], "")
     venv = mocksession.getenv('python')
     assert not hasattr(venv, 'just_created')
-    venv.install_sdist("whatever")
+    mocksession.installsdist(venv, "whatever")
     l = mocksession._pcalls
     assert len(l) == 1
     assert '-U' in l[0].args

File tests/test_z_cmdline.py

             changedir=tests
             commands=
                 py.test --basetemp={envtmpdir} --junitxml=junit-{envname}.xml []
-            deps=py
+            deps=pytest
         '''
     })
     result = cmd.run("tox")
     result = cmd.run("tox", "-v", "--notest")
     assert not result.ret
     result.stdout.fnmatch_lines([
-        "*test summary*",
+        "*summary*",
         "*py25*skipped tests*",
     ])
     result = cmd.run("tox", "-v", "--notest", "-epy25")
     assert not result.ret
     result.stdout.fnmatch_lines([
-        "*py25*prepareenv*",
-        "*reusing*",
+        "*py25*reusing*",
     ])
 
 def test_env_PYTHONDONTWRITEBYTECODE(initproj, cmd, monkeypatch):
 def test_sdistonly(initproj, cmd):
     initproj("example123", filedefs={'tox.ini': """
     """})
-    result = cmd.run("tox", "--sdistonly")
+    result = cmd.run("tox", "-v", "--sdistonly")
     assert not result.ret
     result.stdout.fnmatch_lines([
-        "*using*setup.py*",
+        "*packaging sdist*setup.py*",
     ])
     assert "virtualenv" not in result.stdout.str()
 
 [tox]
-envlist=py27,py26,py25,py24,py31,py32,docs
+envlist=py27,py26,py25,py31,py32,docs
 indexserver =
     testrun = http://pypi.testrun.org
     pypi = http://pypi.python.org/simple

File tox/_cmdline.py

 setup by using virtualenv. Configuration is generally done through an
 INI-style "tox.ini" file.
 """
+from __future__ import with_statement
+
 import tox
 import py
 import os
 import sys
 import subprocess
+import time
 from tox._verlib import NormalizedVersion, IrrationalVersionError
 from tox._venv import VirtualEnv
 from tox._config import parseconfig
         else:
             self.venvname = "GLOB"
 
+    def __enter__(self):
+        self.report.logaction_start(self)
+
+    def __exit__(self, *args):
+        self.report.logaction_finish(self)
+
     def setactivity(self, name, msg):
         self.activity = name
         self.report.verbosity0("%s %s: %s" %(self.venvname, name, msg),
             bold=True)
 
+    def info(self, name, msg):
+        self.report.verbosity1("%s %s: %s" %(self.venvname, name, msg),
+            bold=True)
+
     def _initlogpath(self, actionid):
         if self.venv:
             logdir = self.venv.envconfig.envlogdir
         popen.cwd = cwd
         popen.action = self
         self._popenlist.append(popen)
-        self.report.logpopen(popen)
         try:
-            out, err = popen.communicate()
-        except KeyboardInterrupt:
-            self.report.keyboard_interrupt()
-            popen.wait()
-            raise KeyboardInterrupt()
-        ret = popen.wait()
+            self.report.logpopen(popen)
+            try:
+                out, err = popen.communicate()
+            except KeyboardInterrupt:
+                self.report.keyboard_interrupt()
+                popen.wait()
+                raise KeyboardInterrupt()
+            ret = popen.wait()
+        finally:
+            self._popenlist.remove(popen)
         if ret:
             invoked = " ".join(map(str, popen.args))
             if outpath:
         return self.session.popen(args, shell=shell, cwd=str(cwd),
 	        stdout=stdout, stderr=stderr, env=env)
 
-class Reporter:
+class Reporter(object):
     actionchar = "-"
     def __init__(self, session):
         self.tw = py.io.TerminalWriter()
         else:
             self.verbosity1("  %s$ %s " %(popen.cwd, cmd))
 
-    def logaction(self, action):
-        msg = action.msg
-        msg += " " + " ".join(map(str, action.args))
-        self.logline("%s %s" % (action.venvname, msg,), bold=True)
+    def logaction_start(self, action):
+        msg = action.msg + " " + " ".join(map(str, action.args))
+        self.verbosity1("%s start: %s" %(action.venvname, msg), bold=True)
+        self._starttime = time.time()
+
+    def logaction_finish(self, action):
+        duration = time.time() - self._starttime
+        self.verbosity1("%s finish: %s in %.2f seconds" %(
+            action.venvname, action.msg, duration), bold=True)
+
+    def startsummary(self):
+        self.tw.sep("_", "summary")
 
     def info(self, msg):
         if self.session.config.opts.verbosity >= 2:
         if self.session.config.opts.verbosity >= 1:
             self.tw.line("%s" % msg, **opts)
 
+    def verbosity2(self, msg, **opts):
+        if self.session.config.opts.verbosity >= 2:
+            self.tw.line("%s" % msg, **opts)
+
     #def log(self, msg):
     #    py.builtin.print_(msg, file=sys.stderr)
 
 
     def newaction(self, venv, msg, *args):
         action = Action(self, venv, msg, args)
-        self.report.logaction(action)
         self._actions.append(action)
         return action
 
         self.venvstatus[venv.path] = msg
 
     def _makesdist(self):
-        action = self.newaction(None, "prepare sdist package")
         setup = self.config.setupdir.join("setup.py")
-        action.setactivity("sdist-make", "using %s" % setup)
         if not setup.check():
             raise tox.exception.MissingFile(setup)
-        self.make_emptydir(self.config.distdir)
-        action.popen([sys.executable, setup, "sdist", "--formats=zip",
-                      "--dist-dir", self.config.distdir, ],
-                      cwd=self.config.setupdir)
-        return self.config.distdir.listdir()[0]
+        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)
+            return self.config.distdir.listdir()[0]
 
     def make_emptydir(self, path):
         if path.check():
             py.std.shutil.rmtree(str(path), ignore_errors=True)
             path.ensure(dir=1)
 
-    def setupenv(self, venv, sdist_path):
-        action = self.newaction(venv, "prepareenv", venv.envconfig.envdir)
-        if self._prepareenv(action, venv):
-            if sdist_path is not None:
-                self.installsdist(venv, sdist_path)
-
-    def _prepareenv(self, action, venv):
-        self.venvstatus[venv.path] = 0
-        try:
-            status = venv.update(action=action)
-        except tox.exception.InvocationError:
-            status = sys.exc_info()[1]
-        if status:
-            self.setenvstatus(venv, status)
-            self.report.error(str(status))
-            return False
-        return True
+    def setupenv(self, venv):
+        action = self.newaction(venv, "getenv", venv.envconfig.envdir)
+        with action:
+            self.venvstatus[venv.path] = 0
+            try:
+                status = venv.update(action=action)
+            except tox.exception.InvocationError:
+                status = sys.exc_info()[1]
+            if status:
+                self.setenvstatus(venv, status)
+                self.report.error(str(status))
+                return False
+            return True
 
     def installsdist(self, venv, sdist_path):
-        try:
-            venv.install_sdist(sdist_path)
-        except tox.exception.InvocationError:
-            self.setenvstatus(venv, sys.exc_info()[1])
+        action = self.newaction(venv, "sdist-install", sdist_path)
+        with action:
+            try:
+                venv.install_sdist(sdist_path, action)
+                return True
+            except tox.exception.InvocationError:
+                self.setenvstatus(venv, sys.exc_info()[1])
+                return False
 
     def sdist(self):
         if not self.config.opts.sdistonly and self.config.sdistsrc:
             except tox.exception.InvocationError:
                 v = sys.exc_info()[1]
                 self.report.error("FAIL could not package project")
-                raise SystemExit(1)
+                return
             sdistfile = self.config.distshare.join(sdist_path.basename)
             if sdistfile != sdist_path:
                 self.report.info("copying new sdistfile to %r" %
 
     def subcommand_test(self):
         sdist_path = self.sdist()
+        if not sdist_path:
+            return 2
         if self.config.opts.sdistonly:
             return
         for venv in self.venvlist:
-            self.setupenv(venv, sdist_path)
-            self.runtestenv(venv, sdist_path)
+            if self.setupenv(venv):
+                self.installsdist(venv, sdist_path)
+                self.runtestenv(venv, sdist_path)
         retcode = self._summary()
         return retcode
 
     def runtestenv(self, venv, sdist_path, redirect=False):
         if not self.config.opts.notest:
-            testaction = self.newaction(venv, "runtests")
             if self.venvstatus[venv.path]:
                 return
-            if venv.test(testaction, redirect=redirect):
+            if venv.test(redirect=redirect):
                 self.setenvstatus(venv, "commands failed")
         else:
             self.setenvstatus(venv, "skipped tests")
 
     def _summary(self):
-        action = self.newaction(None, "test summary")
+        self.report.startsummary()
         retcode = 0
         for venv in self.venvlist:
             status = self.venvstatus[venv.path]

File tox/_venv.py

-
+from __future__ import with_statement
 import sys, os
 import py
 import tox
         rconfig = CreationConfig.readconfig(self.path_config)
         if not self.envconfig.recreate and rconfig and \
             rconfig.matches(self._getliveconfig()):
-            action.setactivity("reusing", "existing environment matches")
+            action.info("reusing", self.envconfig.envdir)
             return
         if rconfig is None:
-            action.setactivity("create", "no existing environment found")
+            action.setactivity("create", self.envconfig.envdir)
         else:
-            action.setactivity("recreate",
-                "configchange/incomplete install detected")
+            action.setactivity("recreate", self.envconfig.envdir)
         try:
             self.create(action)
         except tox.exception.UnsupportedInterpreter:
         self._pcall(args, venv=False, action=action, cwd=basepath)
         self.just_created = True
 
-    def install_sdist(self, sdistpath):
+    def install_sdist(self, sdistpath, action):
+        assert action is not None
         if getattr(self, 'just_created', False):
-            action = self.session.newaction(self, "sdist-inst", sdistpath)
+            action.setactivity("sdist-inst", sdistpath)
             self._getliveconfig().writeconfig(self.path_config)
             extraopts = []
         else:
-            action = self.session.newaction(self, "sdist-reinst", sdistpath)
+            action.setactivity("sdist-reinst", sdistpath)
             extraopts = ['-U', '--no-deps']
         self._install([sdistpath], extraopts=extraopts, action=action)
 
             env_arg = None
         return env_arg
 
-    def test(self, action=None, redirect=False):
-        if action is None:
-            action = self.session.newaction(self, "test")
-        self.session.make_emptydir(self.envconfig.envtmpdir)
-        cwd = self.envconfig.changedir
-        for argv in self.envconfig.commands:
-            try:
-                self._pcall(argv, cwd=cwd, action=action, redirect=redirect)
-            except tox.exception.InvocationError:
-                self.session.report.error(str(sys.exc_info()[1]))
-                return True
+    def test(self, redirect=False):
+        action = self.session.newaction(self, "runtests")
+        with action:
+            self.session.make_emptydir(self.envconfig.envtmpdir)
+            cwd = self.envconfig.changedir
+            for argv in self.envconfig.commands:
+                try:
+                    self._pcall(argv, cwd=cwd, action=action, redirect=redirect)
+                except tox.exception.InvocationError:
+                    self.session.report.error(str(sys.exc_info()[1]))
+                    return True
 
     def _pcall(self, args, venv=True, cwd=None, extraenv={},
             action=None, redirect=True):