diff options
author | Vladimir Vitvitskiy <vladimir.vitvitskiy@hp.com> | 2015-04-23 00:18:46 +0300 |
---|---|---|
committer | Vladimir Vitvitskiy <vladimir.vitvitskiy@hp.com> | 2015-04-23 00:18:46 +0300 |
commit | ad220729fd9c08662f2fbc5beda265ddaffe62a0 (patch) | |
tree | 76ad86167ce78fdeaf9eaed20d2646218332626c /tox | |
parent | accadc9fadca83bd9fbe4bc06884d268ff8f9618 (diff) | |
download | tox-ad220729fd9c08662f2fbc5beda265ddaffe62a0.tar.gz |
fix PEP8 violations
* tox.ini updated to run PEP8 checks along with flakes
* added dev test environment to run any command in it or looponfail tests
* fixed all PEP8 violations pytest-pep8 complained about
* line width set to 99
Diffstat (limited to 'tox')
-rw-r--r-- | tox/__init__.py | 4 | ||||
-rw-r--r-- | tox/_cmdline.py | 94 | ||||
-rw-r--r-- | tox/_config.py | 175 | ||||
-rw-r--r-- | tox/_exception.py | 14 | ||||
-rw-r--r-- | tox/_pytestplugin.py | 64 | ||||
-rw-r--r-- | tox/_quickstart.py | 17 | ||||
-rw-r--r-- | tox/_venv.py | 57 | ||||
-rw-r--r-- | tox/_verlib.py | 16 | ||||
-rw-r--r-- | tox/interpreters.py | 24 | ||||
-rw-r--r-- | tox/result.py | 13 |
10 files changed, 278 insertions, 200 deletions
diff --git a/tox/__init__.py b/tox/__init__.py index 95f2dfb..7869fcb 100644 --- a/tox/__init__.py +++ b/tox/__init__.py @@ -1,10 +1,12 @@ # __version__ = '2.0.0.dev1' + class exception: class Error(Exception): def __str__(self): - return "%s: %s" %(self.__class__.__name__, self.args[0]) + return "%s: %s" % (self.__class__.__name__, self.args[0]) + class ConfigError(Error): """ error in tox configuration. """ class UnsupportedInterpreter(Error): diff --git a/tox/_cmdline.py b/tox/_cmdline.py index 5846c7c..0236d17 100644 --- a/tox/_cmdline.py +++ b/tox/_cmdline.py @@ -17,9 +17,11 @@ from tox._config import parseconfig from tox.result import ResultLog from subprocess import STDOUT + def now(): return py.std.time.time() + def main(args=None): try: config = parseconfig(args, 'tox') @@ -28,6 +30,7 @@ def main(args=None): except KeyboardInterrupt: raise SystemExit(2) + class Action(object): def __init__(self, session, venv, msg, args): self.venv = venv @@ -55,12 +58,10 @@ class Action(object): def setactivity(self, name, msg): self.activity = name - self.report.verbosity0("%s %s: %s" %(self.venvname, name, msg), - bold=True) + 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) + self.report.verbosity1("%s %s: %s" % (self.venvname, name, msg), bold=True) def _initlogpath(self, actionid): if self.venv: @@ -83,8 +84,8 @@ class Action(object): resultjson = self.session.config.option.resultjson if resultjson or redirect: fout = self._initlogpath(self.id) - fout.write("actionid=%s\nmsg=%s\ncmdargs=%r\nenv=%s\n" %( - self.id, self.msg, args, env)) + fout.write("actionid=%s\nmsg=%s\ncmdargs=%r\nenv=%s\n" % ( + self.id, self.msg, args, env)) fout.flush() self.popen_outpath = outpath = py.path.local(fout.name) fin = outpath.open() @@ -154,9 +155,9 @@ class Action(object): if hasattr(self, "commandlog"): self.commandlog.add_command(popen.args, out, ret) raise tox.exception.InvocationError( - "%s (see %s)" %(invoked, outpath), ret) + "%s (see %s)" % (invoked, outpath), ret) else: - raise tox.exception.InvocationError("%r" %(invoked, )) + raise tox.exception.InvocationError("%r" % (invoked, )) if not out and outpath: out = outpath.read() if hasattr(self, "commandlog"): @@ -170,8 +171,8 @@ class Action(object): arg = cwd.bestrelpath(arg) newargs.append(str(arg)) - #subprocess does not always take kindly to .py scripts - #so adding the interpreter here. + # subprocess does not always take kindly to .py scripts + # so adding the interpreter here. if sys.platform == "win32": ext = os.path.splitext(str(newargs[0]))[1].lower() if ext == '.py' and self.venv: @@ -184,39 +185,37 @@ class Action(object): if env is None: env = os.environ.copy() return self.session.popen(args, shell=False, cwd=str(cwd), - universal_newlines=True, - stdout=stdout, stderr=stderr, env=env) - + universal_newlines=True, + stdout=stdout, stderr=stderr, env=env) class Reporter(object): actionchar = "-" + def __init__(self, session): self.tw = py.io.TerminalWriter() self.session = session self._reportedlines = [] - #self.cumulated_time = 0.0 + # self.cumulated_time = 0.0 def logpopen(self, popen, env): """ log information about the action.popen() created process. """ cmd = " ".join(map(str, popen.args)) if popen.outpath: - self.verbosity1(" %s$ %s >%s" %(popen.cwd, cmd, - popen.outpath, - )) + self.verbosity1(" %s$ %s >%s" % (popen.cwd, cmd, popen.outpath,)) else: - self.verbosity1(" %s$ %s " %(popen.cwd, cmd)) + self.verbosity1(" %s$ %s " % (popen.cwd, cmd)) def logaction_start(self, action): msg = action.msg + " " + " ".join(map(str, action.args)) - self.verbosity2("%s start: %s" %(action.venvname, msg), bold=True) + self.verbosity2("%s start: %s" % (action.venvname, msg), bold=True) assert not hasattr(action, "_starttime") action._starttime = now() def logaction_finish(self, action): duration = now() - action._starttime - #self.cumulated_time += duration - self.verbosity2("%s finish: %s after %.2f seconds" %( + # self.cumulated_time += duration + self.verbosity2("%s finish: %s after %.2f seconds" % ( action.venvname, action.msg, duration), bold=True) def startsummary(self): @@ -228,8 +227,7 @@ class Reporter(object): def using(self, msg): if self.session.config.option.verbosity >= 1: - self.logline("using %s" %(msg,), bold=True) - + self.logline("using %s" % (msg,), bold=True) def keyboard_interrupt(self): self.error("KEYBOARDINTERRUPT") @@ -275,7 +273,7 @@ class Reporter(object): if self.session.config.option.verbosity >= 2: self.logline("%s" % msg, **opts) - #def log(self, msg): + # def log(self, msg): # py.builtin.print_(msg, file=sys.stderr) @@ -288,13 +286,15 @@ class Session: 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.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] + self.venvlist = [ + self.getvenv(x) + for x in self.config.envlist + ] except LookupError: raise SystemExit(1) self._actions = [] @@ -321,15 +321,14 @@ class Session: return action def runcommand(self): - self.report.using("tox-%s from %s" %(tox.__version__, - tox.__file__)) + 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)) + "tox version is %s, required is at least %s" % ( + toxversion, minversion)) raise SystemExit(1) if self.config.option.showconfig: self.showconfig() @@ -342,7 +341,7 @@ class Session: for relpath in pathlist: src = srcdir.join(relpath) if not src.check(): - self.report.error("missing source file: %s" %(src,)) + self.report.error("missing source file: %s" % (src,)) raise SystemExit(1) target = destdir.join(relpath) target.dirpath().ensure(dir=1) @@ -358,7 +357,7 @@ class Session: self.make_emptydir(self.config.distdir) action.popen([sys.executable, setup, "sdist", "--formats=zip", "--dist-dir", self.config.distdir, ], - cwd=self.config.setupdir) + cwd=self.config.setupdir) try: return self.config.distdir.listdir()[0] except py.error.ENOENT: @@ -375,12 +374,11 @@ class Session: ) raise SystemExit(1) self.report.error( - 'No dist directory found. Please check setup.py, e.g with:\n'\ + '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) @@ -446,25 +444,25 @@ class Session: :rtype: py.path.local """ if not self.config.option.sdistonly and (self.config.sdistsrc or - self.config.option.installpkg): + 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)) + 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 - v = %r" % - v) + v) return sdistfile = self.config.distshare.join(sdist_path.basename) if sdistfile != sdist_path: self.report.info("copying new sdistfile to %r" % - str(sdistfile)) + str(sdistfile)) try: sdistfile.dirpath().ensure(dir=1) except py.error.Error: @@ -513,24 +511,23 @@ class Session: for venv in self.venvlist: status = venv.status if isinstance(status, tox.exception.InterpreterNotFound): - msg = " %s: %s" %(venv.envconfig.envname, str(status)) + 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)) + msg = " %s: %s" % (venv.envconfig.envname, str(status)) self.report.verbosity1(msg) elif status and status != "skipped tests": - msg = " %s: %s" %(venv.envconfig.envname, str(status)) + 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)) + self.report.good(" %s: %s" % (venv.envconfig.envname, status)) if not retcode: self.report.good(" congratulations :)") @@ -586,7 +583,6 @@ class Session: versions.append("virtualenv-%s" % version.strip()) self.report.keyvalue("tool-versions:", " ".join(versions)) - def _resolve_pkg(self, pkgspec): try: return self._spec2pkg[pkgspec] @@ -614,7 +610,7 @@ class Session: items.append((ver, x)) else: self.report.warning("could not determine version of: %s" % - str(x)) + str(x)) items.sort() if not items: raise tox.exception.MissingDependency(pkgspec) @@ -624,6 +620,8 @@ class Session: _rex_getversion = py.std.re.compile("[\w_\-\+\.]+-(.*)(\.zip|\.tar.gz)") + + def getversion(basename): m = _rex_getversion.match(basename) if m is None: diff --git a/tox/_config.py b/tox/_config.py index 9d054a0..afd2c55 100644 --- a/tox/_config.py +++ b/tox/_config.py @@ -45,7 +45,7 @@ def parseconfig(args=None, pkg=None): if inipath.check(): break else: - feedback("toxini file %r not found" %(basename), sysexit=True) + feedback("toxini file %r not found" % (basename), sysexit=True) try: parseini(config, inipath) except tox.exception.InterpreterNotFound: @@ -54,98 +54,104 @@ def parseconfig(args=None, pkg=None): py.builtin.print_("ERROR: " + str(exn)) return config + def feedback(msg, sysexit=False): py.builtin.print_("ERROR: " + msg, file=sys.stderr) if sysexit: raise SystemExit(1) + class VersionAction(argparse.Action): def __call__(self, argparser, *args, **kwargs): name = argparser.pkgname mod = __import__(name) version = mod.__version__ - py.builtin.print_("%s imported from %s" %(version, mod.__file__)) + py.builtin.print_("%s imported from %s" % (version, mod.__file__)) raise SystemExit(0) + class CountAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): if hasattr(namespace, self.dest): - setattr(namespace, self.dest, int(getattr(namespace, self.dest))+1) + setattr(namespace, self.dest, int(getattr(namespace, self.dest)) + 1) else: setattr(namespace, self.dest, 0) + def prepare_parse(pkgname): parser = argparse.ArgumentParser(description=__doc__,) - #formatter_class=argparse.ArgumentDefaultsHelpFormatter) + # formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.pkgname = pkgname parser.add_argument("--version", nargs=0, action=VersionAction, - dest="version", - help="report version information to stdout.") + dest="version", + help="report version information to stdout.") parser.add_argument("-v", nargs=0, action=CountAction, default=0, - dest="verbosity", - help="increase verbosity of reporting output.") + dest="verbosity", + help="increase verbosity of reporting output.") parser.add_argument("--showconfig", action="store_true", - help="show configuration information for all environments. ") + help="show configuration information for all environments. ") parser.add_argument("-l", "--listenvs", action="store_true", - dest="listenvs", help="show list of test environments") + dest="listenvs", help="show list of test environments") parser.add_argument("-c", action="store", default="tox.ini", - dest="configfile", - help="use the specified config file name.") + dest="configfile", + help="use the specified config file name.") parser.add_argument("-e", action="append", dest="env", - metavar="envlist", - help="work against specified environments (ALL selects all).") + metavar="envlist", + help="work against specified environments (ALL selects all).") parser.add_argument("--notest", action="store_true", dest="notest", - help="skip invoking test commands.") + help="skip invoking test commands.") parser.add_argument("--sdistonly", action="store_true", dest="sdistonly", - help="only perform the sdist packaging activity.") + help="only perform the sdist packaging activity.") parser.add_argument("--installpkg", action="store", default=None, - metavar="PATH", - help="use specified package for installation into venv, instead of " - "creating an sdist.") + metavar="PATH", + help="use specified package for installation into venv, instead of " + "creating an sdist.") parser.add_argument("--develop", action="store_true", dest="develop", - help="install package in the venv using 'setup.py develop' via " - "'pip -e .'") + help="install package in the venv using 'setup.py develop' via " + "'pip -e .'") parser.add_argument("--set-home", action="store_true", dest="sethome", - help="(experimental) force creating a new $HOME for each test " - "environment and create .pydistutils.cfg|pip.conf files " - "if index servers are specified with tox. ") + help="(experimental) force creating a new $HOME for each test " + "environment and create .pydistutils.cfg|pip.conf files " + "if index servers are specified with tox. ") parser.add_argument('-i', action="append", - dest="indexurl", metavar="URL", - help="set indexserver url (if URL is of form name=url set the " - "url for the 'name' indexserver, specifically)") + dest="indexurl", metavar="URL", + help="set indexserver url (if URL is of form name=url set the " + "url for the 'name' indexserver, specifically)") parser.add_argument("--pre", action="store_true", dest="pre", - help="install pre-releases and development versions of dependencies. " - "This will pass the --pre option to install_command (pip by default).") + help="install pre-releases and development versions of dependencies. " + "This will pass the --pre option to install_command " + "(pip by default).") parser.add_argument("-r", "--recreate", action="store_true", - dest="recreate", - help="force recreation of virtual environments") + dest="recreate", + help="force recreation of virtual environments") parser.add_argument("--result-json", action="store", - dest="resultjson", metavar="PATH", - help="write a json file with detailed information about " - "all commands and results involved. This will turn off " - "pass-through output from running test commands which is " - "instead captured into the json result file.") + dest="resultjson", metavar="PATH", + help="write a json file with detailed information about " + "all commands and results involved. This will turn off " + "pass-through output from running test commands which is " + "instead captured into the json result file.") # We choose 1 to 4294967295 because it is the range of PYTHONHASHSEED. parser.add_argument("--hashseed", action="store", - metavar="SEED", default=None, - help="set PYTHONHASHSEED to SEED before running commands. " - "Defaults to a random integer in the range [1, 4294967295] " - "([1, 1024] on Windows). " - "Passing 'noset' suppresses this behavior.") + metavar="SEED", default=None, + help="set PYTHONHASHSEED to SEED before running commands. " + "Defaults to a random integer in the range [1, 4294967295] " + "([1, 1024] on Windows). " + "Passing 'noset' suppresses this behavior.") parser.add_argument("--force-dep", action="append", - metavar="REQ", default=None, - help="Forces a certain version of one of the dependencies " - "when configuring the virtual environment. REQ Examples " - "'pytest<2.7' or 'django>=1.6'.") + metavar="REQ", default=None, + help="Forces a certain version of one of the dependencies " + "when configuring the virtual environment. REQ Examples " + "'pytest<2.7' or 'django>=1.6'.") parser.add_argument("--sitepackages", action="store_true", - help="override sitepackages setting to True in all envs") + help="override sitepackages setting to True in all envs") parser.add_argument("--skip-missing-interpreters", action="store_true", - help="don't fail tests for missing interpreters") + help="don't fail tests for missing interpreters") parser.add_argument("args", nargs="*", - help="additional arguments available to command positional substitution") + help="additional arguments available to command positional substitution") return parser + class Config(object): def __init__(self): self.envconfigs = {} @@ -167,8 +173,9 @@ class VenvConfig: @property def envbindir(self): - if (sys.platform == "win32" and "jython" not in self.basepython - and "pypy" not in self.basepython): + if (sys.platform == "win32" + and "jython" not in self.basepython + and "pypy" not in self.basepython): return self.envdir.join("Scripts") else: return self.envdir.join("bin") @@ -185,8 +192,8 @@ class VenvConfig: def envsitepackagesdir(self): self.getsupportedinterpreter() # for throwing exceptions x = self.config.interpreters.get_sitepackagesdir( - info=self._basepython_info, - envdir=self.envdir) + info=self._basepython_info, + envdir=self.envdir) return x def getsupportedinterpreter(self): @@ -200,27 +207,29 @@ class VenvConfig: if not info.version_info: raise tox.exception.InvocationError( 'Failed to get version_info for %s: %s' % (info.name, info.err)) - if info.version_info < (2,6): + if info.version_info < (2, 6): raise tox.exception.UnsupportedInterpreter( "python2.5 is not supported anymore, sorry") return info.executable - testenvprefix = "testenv:" + def get_homedir(): try: return py.path.local._gethomedir() except Exception: return None + def make_hashseed(): max_seed = 4294967295 if sys.platform == 'win32': max_seed = 1024 return str(random.randint(1, max_seed)) + class parseini: def __init__(self, config, inipath): config.toxinipath = inipath @@ -287,8 +296,7 @@ class parseini: config.indexserver[name] = IndexServerConfig(name, override) reader.addsubstitutions(toxworkdir=config.toxworkdir) - config.distdir = reader.getpath(toxsection, "distdir", - "{toxworkdir}/dist") + config.distdir = reader.getpath(toxsection, "distdir", "{toxworkdir}/dist") reader.addsubstitutions(distdir=config.distdir) config.distshare = reader.getpath(toxsection, "distshare", distshare_default) @@ -335,18 +343,18 @@ class parseini: def _makeenvconfig(self, name, section, subs, config): vc = VenvConfig(config=config, envname=name) factors = set(name.split('-')) - reader = IniReader(self._cfg, fallbacksections=["testenv"], - factors=factors) + reader = IniReader(self._cfg, fallbacksections=["testenv"], factors=factors) reader.addsubstitutions(**subs) - vc.develop = not config.option.installpkg and \ - reader.getbool(section, "usedevelop", config.option.develop) + vc.develop = ( + not config.option.installpkg + and reader.getbool(section, "usedevelop", config.option.develop)) vc.envdir = reader.getpath(section, "envdir", "{toxworkdir}/%s" % name) vc.args_are_paths = reader.getbool(section, "args_are_paths", True) if reader.getdefault(section, "python", None): raise tox.exception.ConfigError( "'python=' key was renamed to 'basepython='") bp = next((default_factors[f] for f in factors if f in default_factors), - sys.executable) + sys.executable) vc.basepython = reader.getdefault(section, "basepython", bp) vc._basepython_info = config.interpreters.get_info(vc.basepython) reader.addsubstitutions(envdir=vc.envdir, envname=vc.envname, @@ -410,8 +418,9 @@ class parseini: break vc.platform = platform - vc.sitepackages = self.config.option.sitepackages or \ - reader.getbool(section, "sitepackages", False) + vc.sitepackages = ( + self.config.option.sitepackages + or reader.getbool(section, "sitepackages", False)) vc.downloadcache = None downloadcache = reader.getdefault(section, "downloadcache") @@ -424,10 +433,10 @@ class parseini: section, "install_command", "pip install {opts} {packages}", - ) + ) if '{packages}' not in vc.install_command: raise tox.exception.ConfigError( - "'install_command' must contain '{packages}' substitution") + "'install_command' must contain '{packages}' substitution") vc.pip_pre = config.option.pre or reader.getbool( section, "pip_pre", False) @@ -488,10 +497,12 @@ def _split_env(env): env = [env] return mapcat(_expand_envstr, env) + def _split_factor_expr(expr): partial_envs = _expand_envstr(expr) return [set(e.split('-')) for e in partial_envs] + def _expand_envstr(envstr): # split by commas not in groups tokens = re.split(r'((?:\{[^}]+\})+)|,', envstr) @@ -505,9 +516,11 @@ def _expand_envstr(envstr): return mapcat(expand, envlist) + def mapcat(f, seq): return list(itertools.chain.from_iterable(map(f, seq))) + class DepConfig: def __init__(self, name, indexserver=None): self.name = name @@ -710,7 +723,7 @@ class IniReader: x = self._replace(x) finally: assert self._subststack.pop() == (section, name) - #print "getdefault", section, name, "returned", repr(x) + # print "getdefault", section, name, "returned", repr(x) return x def _apply_factors(self, s): @@ -740,7 +753,7 @@ class IniReader: else: envkey = match_value - if not envkey in os.environ and default is None: + if envkey not in os.environ and default is None: raise tox.exception.ConfigError( "substitution env:%r: unkown environment variable %r" % (envkey, envkey)) @@ -750,11 +763,11 @@ class IniReader: def _substitute_from_other_section(self, key): if key.startswith("[") and "]" in key: i = key.find("]") - section, item = key[1:i], key[i+1:] + section, item = key[1:i], key[i + 1:] if section in self._cfg and item in self._cfg[section]: if (section, item) in self._subststack: - raise ValueError('%s already in %s' %( - (section, item), self._subststack)) + raise ValueError('%s already in %s' % ( + (section, item), self._subststack)) x = str(self._cfg[section][item]) self._subststack.append((section, item)) try: @@ -785,13 +798,14 @@ class IniReader: return '{%s}' % sub_value handlers = { - 'env' : self._replace_env, - None : self._replace_substitution, - } + 'env': self._replace_env, + None: self._replace_substitution, + } try: sub_type = g['sub_type'] except KeyError: - raise tox.exception.ConfigError("Malformed substitution; no substitution type provided") + raise tox.exception.ConfigError( + "Malformed substitution; no substitution type provided") try: handler = handlers[sub_type] @@ -808,6 +822,7 @@ class IniReader: def _parse_command(self, command): pass + class CommandParser(object): class State(object): @@ -824,11 +839,11 @@ class CommandParser(object): def word_has_ended(): return ((cur_char in string.whitespace and ps.word and - ps.word[-1] not in string.whitespace) or - (cur_char == '{' and ps.depth == 0 and not ps.word.endswith('\\')) or - (ps.depth == 0 and ps.word and ps.word[-1] == '}') or - (cur_char not in string.whitespace and ps.word and - ps.word.strip() == '')) + ps.word[-1] not in string.whitespace) or + (cur_char == '{' and ps.depth == 0 and not ps.word.endswith('\\')) or + (ps.depth == 0 and ps.word and ps.word[-1] == '}') or + (cur_char not in string.whitespace and ps.word and + ps.word.strip() == '')) def yield_this_word(): yieldword = ps.word @@ -869,8 +884,8 @@ class CommandParser(object): yield_this_word() return ps.yield_words + def getcontextname(): if any(env in os.environ for env in ['JENKINS_URL', 'HUDSON_URL']): return 'jenkins' return None - diff --git a/tox/_exception.py b/tox/_exception.py index 5244171..9917f09 100644 --- a/tox/_exception.py +++ b/tox/_exception.py @@ -1,16 +1,28 @@ class Error(Exception): def __str__(self): - return "%s: %s" %(self.__class__.__name__, self.args[0]) + return "%s: %s" % (self.__class__.__name__, self.args[0]) + + class UnsupportedInterpreter(Error): "signals an unsupported Interpreter" + + class InterpreterNotFound(Error): "signals that an interpreter could not be found" + + class InvocationError(Error): """ an error while invoking a script. """ + + class MissingFile(Error): """ an error while invoking a script. """ + + class MissingDirectory(Error): """ a directory did not exist. """ + + class MissingDependency(Error): """ a dependency could not be found or determined. """ diff --git a/tox/_pytestplugin.py b/tox/_pytestplugin.py index ded13ec..9d7488a 100644 --- a/tox/_pytestplugin.py +++ b/tox/_pytestplugin.py @@ -1,4 +1,5 @@ -import pytest, py +import py +import pytest import tox import os import sys @@ -10,6 +11,7 @@ from tox._venv import VirtualEnv from tox._cmdline import Action from tox.result import ResultLog + def pytest_configure(): if 'TOXENV' in os.environ: del os.environ['TOXENV'] @@ -19,12 +21,14 @@ def pytest_configure(): def pytest_addoption(parser): parser.addoption("--no-network", action="store_true", - dest="no_network", - help="don't run tests requiring network") + dest="no_network", + help="don't run tests requiring network") + def pytest_report_header(): return "tox comes from: %r" % (tox.__file__) + @pytest.fixture def newconfig(request, tmpdir): def newconfig(args, source=None): @@ -41,12 +45,14 @@ def newconfig(request, tmpdir): old.chdir() return newconfig + @pytest.fixture def cmd(request): if request.config.option.no_network: pytest.skip("--no-network was specified, test cannot run") return Cmd(request) + class ReportExpectMock: def __init__(self, session): self._calls = [] @@ -61,13 +67,13 @@ class ReportExpectMock: raise AttributeError(name) def generic_report(*args, **kwargs): - self._calls.append((name,)+args) - print ("%s" %(self._calls[-1], )) + self._calls.append((name,) + args) + print ("%s" % (self._calls[-1], )) return generic_report def action(self, venv, msg, *args): self._calls.append(("action", venv, msg)) - print ("%s" %(self._calls[-1], )) + print ("%s" % (self._calls[-1], )) return Action(self.session, venv, msg, args) def getnext(self, cat): @@ -82,7 +88,7 @@ class ReportExpectMock: newindex += 1 raise LookupError( "looking for %r, no reports found at >=%d in %r" % - (cat, self._index+1, self._calls)) + (cat, self._index + 1, self._calls)) def expect(self, cat, messagepattern="*", invert=False): __tracebackhide__ = True @@ -98,16 +104,17 @@ class ReportExpectMock: if fnmatch(lmsg, messagepattern): if invert: raise AssertionError("found %s(%r), didn't expect it" % - (cat, messagepattern)) + (cat, messagepattern)) return if not invert: raise AssertionError( - "looking for %s(%r), no reports found at >=%d in %r" % - (cat, messagepattern, self._index+1, self._calls)) + "looking for %s(%r), no reports found at >=%d in %r" % + (cat, messagepattern, self._index + 1, self._calls)) def not_expect(self, cat, messagepattern="*"): return self.expect(cat, messagepattern, invert=True) + class pcallMock: def __init__(self, args, cwd, env, stdout, stderr, shell): self.arg0 = args[0] @@ -120,44 +127,54 @@ class pcallMock: def communicate(self): return "", "" + def wait(self): pass + @pytest.fixture def mocksession(request): from tox._cmdline import Session + class MockSession(Session): def __init__(self): self._clearmocks() self.config = request.getfuncargvalue("newconfig")([], "") self.resultlog = ResultLog() self._actions = [] + def getenv(self, name): return VirtualEnv(self.config.envconfigs[name], session=self) + def _clearmocks(self): self._pcalls = [] self._spec2pkg = {} self.report = ReportExpectMock(self) + def make_emptydir(self, path): pass + def popen(self, args, cwd, shell=None, - universal_newlines=False, - stdout=None, stderr=None, env=None): + universal_newlines=False, + stdout=None, stderr=None, env=None): pm = pcallMock(args, cwd, env, stdout, stderr, shell) self._pcalls.append(pm) return pm return MockSession() + @pytest.fixture def newmocksession(request): mocksession = request.getfuncargvalue("mocksession") newconfig = request.getfuncargvalue("newconfig") + def newmocksession(args, source): config = newconfig(args, source) mocksession.config = config return mocksession return newmocksession + class Cmd: def __init__(self, request): self.tmpdir = request.getfuncargvalue("tmpdir") @@ -175,7 +192,7 @@ class Cmd: env['PYTHONPATH'] = ":".join(filter(None, [ str(os.getcwd()), env.get('PYTHONPATH', '')])) kw['env'] = env - #print "env", env + # print "env", env return py.std.subprocess.Popen(argv, stdout=stdout, stderr=stderr, **kw) def run(self, *argv): @@ -188,7 +205,7 @@ class Cmd: f2 = p2.open("wb") now = time.time() popen = self.popen(argv, stdout=f1, stderr=f2, - close_fds=(sys.platform != "win32")) + close_fds=(sys.platform != "win32")) ret = popen.wait() f1.close() f2.close() @@ -196,6 +213,7 @@ class Cmd: out = getdecoded(out).splitlines() err = p2.read("rb") err = getdecoded(err).splitlines() + def dump_lines(lines, fp): try: for line in lines: @@ -204,14 +222,16 @@ class Cmd: print("couldn't print to %s because of encoding" % (fp,)) dump_lines(out, sys.stdout) dump_lines(err, sys.stderr) - return RunResult(ret, out, err, time.time()-now) + return RunResult(ret, out, err, time.time() - now) + def getdecoded(out): try: return out.decode("utf-8") except UnicodeDecodeError: return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % ( - py.io.saferepr(out),) + py.io.saferepr(out),) + class RunResult: def __init__(self, ret, outlines, errlines, duration): @@ -222,8 +242,9 @@ class RunResult: self.stderr = LineMatcher(errlines) self.duration = duration + class LineMatcher: - def __init__(self, lines): + def __init__(self, lines): self.lines = lines def str(self): @@ -260,6 +281,7 @@ class LineMatcher: else: assert line == nextline + @pytest.fixture def initproj(request, tmpdir): """ create a factory function for creating example projects. """ @@ -288,16 +310,18 @@ def initproj(request, tmpdir): ) ''' % locals()}) if name not in filedefs: - create_files(base, {name: - {'__init__.py': '__version__ = %r' % version}}) + create_files(base, { + name: {'__init__.py': '__version__ = %r' % version} + }) manifestlines = [] for p in base.visit(lambda x: x.check(file=1)): manifestlines.append("include %s" % p.relto(base)) create_files(base, {"MANIFEST.in": "\n".join(manifestlines)}) - print ("created project in %s" %(base,)) + print ("created project in %s" % (base,)) base.chdir() return initproj + def create_files(base, filedefs): for key, value in filedefs.items(): if isinstance(value, dict): diff --git a/tox/_quickstart.py b/tox/_quickstart.py index e7c4f03..a397cb1 100644 --- a/tox/_quickstart.py +++ b/tox/_quickstart.py @@ -78,11 +78,13 @@ deps = %(deps)s class ValidationError(Exception): """Raised for validation errors.""" + def nonempty(x): if not x: raise ValidationError("Please enter some text.") return x + def choice(*l): def val(x): if x not in l: @@ -90,17 +92,20 @@ def choice(*l): return x return val + def boolean(x): if x.upper() not in ('Y', 'YES', 'N', 'NO'): raise ValidationError("Please enter either 'y' or 'n'.") return x.upper() in ('Y', 'YES') + def suffix(x): if not (x[0:1] == '.' and len(x) > 1): raise ValidationError("Please enter a file suffix, " "e.g. '.rst' or '.txt'.") return x + def ok(x): return x @@ -159,7 +164,7 @@ What Python versions do you want to test against? Choices: [3] (All versions) %s [4] Choose each one-by-one''' % ', '.join(all_envs)) do_prompt(d, 'canned_pyenvs', 'Enter the number of your choice', - '3', choice('1', '2', '3', '4')) + '3', choice('1', '2', '3', '4')) if d['canned_pyenvs'] == '1': d['py27'] = True @@ -197,17 +202,20 @@ What extra dependencies do your tests have?''') def process_input(d): d['envlist'] = ', '.join([env for env in all_envs if d.get(env) is True]) - d['deps'] = '\n' + '\n'.join([' %s' % dep.strip() - for dep in d['deps'].split(',')]) + d['deps'] = '\n' + '\n'.join([ + ' %s' % dep.strip() + for dep in d['deps'].split(',')]) return d + def rtrim_right(text): lines = [] for line in text.split("\n"): lines.append(line.rstrip()) return "\n".join(lines) + def generate(d, overwrite=True, silent=False): """Generate project based on values in *d*.""" @@ -236,7 +244,8 @@ def generate(d, overwrite=True, silent=False): if silent: return sys.stdout.write('\n') - print('Finished: A tox.ini file has been created. For information on this file, see http://tox.testrun.org/latest/config.html') + print('Finished: A tox.ini file has been created. For information on this file, ' + 'see http://tox.testrun.org/latest/config.html') print(''' Execute `tox` to test your project. ''') diff --git a/tox/_venv.py b/tox/_venv.py index 926a893..f642be0 100644 --- a/tox/_venv.py +++ b/tox/_venv.py @@ -1,11 +1,13 @@ from __future__ import with_statement -import sys, os +import os +import sys import re import codecs import py import tox from tox._config import DepConfig + class CreationConfig: def __init__(self, md5, python, version, sitepackages, develop, deps): @@ -43,11 +45,12 @@ class CreationConfig: def matches(self, other): return (other and self.md5 == other.md5 - and self.python == other.python - and self.version == other.version - and self.sitepackages == other.sitepackages - and self.develop == other.develop - and self.deps == other.deps) + and self.python == other.python + and self.version == other.version + and self.sitepackages == other.sitepackages + and self.develop == other.develop + and self.deps == other.deps) + class VirtualEnv(object): def __init__(self, envconfig=None, session=None): @@ -61,7 +64,7 @@ class VirtualEnv(object): return self.envconfig.envname def __repr__(self): - return "<VirtualEnv at %r>" %(self.path) + return "<VirtualEnv at %r>" % (self.path) def getcommandpath(self, name=None, venv=True, cwd=None): if name is None: @@ -81,7 +84,7 @@ class VirtualEnv(object): p = py.path.local.sysfind(name) if p is None: raise tox.exception.InvocationError( - "could not find executable %r" % (name,)) + "could not find executable %r" % (name,)) # p is not found in virtualenv script/bin dir if venv: if not self.is_allowed_external(p): @@ -89,15 +92,16 @@ class VirtualEnv(object): "test command found but not installed in testenv\n" " cmd: %s\n" " env: %s\n" - "Maybe forgot to specify a dependency?" % (p, - self.envconfig.envdir)) - return str(p) # will not be rewritten for reporting + "Maybe forgot to specify a dependency?" % (p, self.envconfig.envdir)) + return str(p) # will not be rewritten for reporting def is_allowed_external(self, p): tryadd = [""] if sys.platform == "win32": - tryadd += [os.path.normcase(x) - for x in os.environ['PATHEXT'].split(os.pathsep)] + tryadd += [ + os.path.normcase(x) + for x in os.environ['PATHEXT'].split(os.pathsep) + ] p = py.path.local(os.path.normcase(str(p))) for x in self.envconfig.whitelist_externals: for add in tryadd: @@ -116,7 +120,7 @@ class VirtualEnv(object): action = self.session.newaction(self, "update") rconfig = CreationConfig.readconfig(self.path_config) if not self.envconfig.recreate and rconfig and \ - rconfig.matches(self._getliveconfig()): + rconfig.matches(self._getliveconfig()): action.info("reusing", self.envconfig.envdir) return if rconfig is None: @@ -167,7 +171,7 @@ class VirtualEnv(object): return re.match(self.envconfig.platform, sys.platform) def create(self, action=None): - #if self.getcommandpath("activate").dirpath().check(): + # if self.getcommandpath("activate").dirpath().check(): # return if action is None: action = self.session.newaction(self, "create") @@ -179,11 +183,11 @@ class VirtualEnv(object): # add interpreter explicitly, to prevent using # default (virtualenv.ini) args.extend(['--python', str(config_interpreter)]) - #if sys.platform == "win32": + # if sys.platform == "win32": # f, path, _ = py.std.imp.find_module("virtualenv") # f.close() # args[:1] = [str(config_interpreter), str(path)] - #else: + # else: self.session.make_emptydir(self.path) basepath = self.path.dirpath() basepath.ensure(dir=1) @@ -191,7 +195,6 @@ class VirtualEnv(object): self._pcall(args, venv=False, action=action, cwd=basepath) self.just_created = True - def finish(self): self._getliveconfig().writeconfig(self.path_config) @@ -204,8 +207,8 @@ class VirtualEnv(object): name = output.strip() egg_info = setupdir.join('.'.join((name, 'egg-info'))) for conf_file in (setup_py, setup_cfg): - if (not egg_info.check() or (conf_file.check() - and conf_file.mtime() > egg_info.mtime())): + if (not egg_info.check() + or (conf_file.check() and conf_file.mtime() > egg_info.mtime())): return True return False @@ -240,8 +243,7 @@ class VirtualEnv(object): deps = self._getresolvedeps() if deps: depinfo = ", ".join(map(str, deps)) - action.setactivity("installdeps", - "%s" % depinfo) + action.setactivity("installdeps", "%s" % depinfo) self._install(deps, action=action) def _installopts(self, indexserver): @@ -261,10 +263,10 @@ class VirtualEnv(object): argv = self.envconfig.install_command[:] # use pip-script on win32 to avoid the executable locking i = argv.index('{packages}') - argv[i:i+1] = packages + argv[i:i + 1] = packages if '{opts}' in argv: i = argv.index('{opts}') - argv[i:i+1] = list(options) + argv[i:i + 1] = list(options) for x in ('PIP_RESPECT_VIRTUALENV', 'PIP_REQUIRE_VIRTUALENV', '__PYVENV_LAUNCHER__'): try: @@ -301,7 +303,7 @@ class VirtualEnv(object): if self.envconfig.config.option.sethome: extraenv = hack_home_env( homedir=self.envconfig.envtmpdir.join("pseudo-home"), - index_url = ixserver.url) + index_url=ixserver.url) else: extraenv = {} @@ -351,7 +353,8 @@ class VirtualEnv(object): ignore_ret = False try: - self._pcall(argv, cwd=cwd, action=action, redirect=redirect, ignore_ret=ignore_ret) + self._pcall(argv, cwd=cwd, action=action, redirect=redirect, + ignore_ret=ignore_ret) except tox.exception.InvocationError: val = sys.exc_info()[1] self.session.report.error(str(val)) @@ -362,7 +365,7 @@ class VirtualEnv(object): raise def _pcall(self, args, venv=True, cwd=None, extraenv={}, - action=None, redirect=True, ignore_ret=False): + action=None, redirect=True, ignore_ret=False): for name in ("VIRTUALENV_PYTHON", "PYTHONDONTWRITEBYTECODE"): try: del os.environ[name] diff --git a/tox/_verlib.py b/tox/_verlib.py index a234176..a2e7471 100644 --- a/tox/_verlib.py +++ b/tox/_verlib.py @@ -10,10 +10,12 @@ licensed under the PSF license (i guess) import re + class IrrationalVersionError(Exception): """This is an irrational version.""" pass + class HugeMajorVersionNumError(IrrationalVersionError): """An irrational version because the major version number is huge (often because a year or date was used). @@ -52,6 +54,7 @@ VERSION_RE = re.compile(r''' (?P<postdev>(\.post(?P<post>\d+))?(\.dev(?P<dev>\d+))?)? $''', re.VERBOSE) + class NormalizedVersion(object): """A rational version. @@ -136,7 +139,8 @@ class NormalizedVersion(object): parts.append(FINAL_MARKER) self.parts = tuple(parts) if error_on_huge_major_num and self.parts[0][0] > 1980: - raise HugeMajorVersionNumError("huge major version number, %r, " + raise HugeMajorVersionNumError( + "huge major version number, %r, " "which might cause future problems: %r" % (self.parts[0][0], s)) def _parse_numdots(self, s, full_ver_str, drop_trailing_zeros=True, @@ -154,7 +158,8 @@ class NormalizedVersion(object): nums = [] for n in s.split("."): if len(n) > 1 and n[0] == '0': - raise IrrationalVersionError("cannot have leading zero in " + raise IrrationalVersionError( + "cannot have leading zero in " "version number segment: '%s' in %r" % (n, full_ver_str)) nums.append(int(n)) if drop_trailing_zeros: @@ -193,7 +198,7 @@ class NormalizedVersion(object): def _cannot_compare(self, other): raise TypeError("cannot compare %s and %s" - % (type(self).__name__, type(other).__name__)) + % (type(self).__name__, type(other).__name__)) def __eq__(self, other): if not isinstance(other, NormalizedVersion): @@ -217,6 +222,7 @@ class NormalizedVersion(object): def __ge__(self, other): return self.__eq__(other) or self.__gt__(other) + def suggest_normalized_version(s): """Suggest a normalized version close to the given version string. @@ -272,7 +278,7 @@ def suggest_normalized_version(s): rs = rs[1:] # Clean leading '0's on numbers. - #TODO: unintended side-effect on, e.g., "2003.05.09" + # TODO: unintended side-effect on, e.g., "2003.05.09" # PyPI stats: 77 (~2%) better rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) @@ -318,7 +324,6 @@ def suggest_normalized_version(s): # PyPI stats: ~21 (0.62%) better rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) - # Tcl/Tk uses "px" for their post release markers rs = re.sub(r"p(\d+)$", r".post\1", rs) @@ -328,4 +333,3 @@ def suggest_normalized_version(s): except IrrationalVersionError: pass return None - diff --git a/tox/interpreters.py b/tox/interpreters.py index 241651f..76075b8 100644 --- a/tox/interpreters.py +++ b/tox/interpreters.py @@ -3,6 +3,7 @@ import py import re import inspect + class Interpreters: def __init__(self): self.name2executable = {} @@ -42,20 +43,21 @@ class Interpreters: envdir = str(envdir) try: res = exec_on_interpreter(info.executable, - [inspect.getsource(sitepackagesdir), - "print (sitepackagesdir(%r))" % envdir]) + [inspect.getsource(sitepackagesdir), + "print (sitepackagesdir(%r))" % envdir]) except ExecFailed: val = sys.exc_info()[1] - print ("execution failed: %s -- %s" %(val.out, val.err)) + print ("execution failed: %s -- %s" % (val.out, val.err)) return "" else: return res["dir"] + def run_and_get_interpreter_info(name, executable): assert executable try: result = exec_on_interpreter(executable, - [inspect.getsource(pyinfo), "print (pyinfo())"]) + [inspect.getsource(pyinfo), "print (pyinfo())"]) except ExecFailed: val = sys.exc_info()[1] return NoInterpreterInfo(name, executable=val.executable, @@ -63,6 +65,7 @@ def run_and_get_interpreter_info(name, executable): else: return InterpreterInfo(name, executable, **result) + def exec_on_interpreter(executable, source): if isinstance(source, list): source = "\n".join(source) @@ -80,6 +83,7 @@ def exec_on_interpreter(executable, source): "could not decode %r" % out) return result + class ExecFailed(Exception): def __init__(self, executable, source, out, err): self.executable = executable @@ -87,6 +91,7 @@ class ExecFailed(Exception): self.out = out self.err = err + class InterpreterInfo: runnable = True @@ -99,10 +104,12 @@ class InterpreterInfo: def __str__(self): return "<executable at %s, version_info %s>" % ( - self.executable, self.version_info) + self.executable, self.version_info) + class NoInterpreterInfo: runnable = False + def __init__(self, name, executable=None, out=None, err="not found"): self.name = name @@ -124,9 +131,10 @@ if sys.platform != "win32": else: # Exceptions to the usual windows mapping win32map = { - 'python': sys.executable, - 'jython': "c:\jython2.5.1\jython.bat", + 'python': sys.executable, + 'jython': "c:\jython2.5.1\jython.bat", } + def locate_via_py(v_maj, v_min): ver = "-%s.%s" % (v_maj, v_min) script = "import sys; print(sys.executable)" @@ -162,11 +170,13 @@ else: if m: return locate_via_py(*m.groups()) + def pyinfo(): import sys return dict(version_info=tuple(sys.version_info), sysplatform=sys.platform) + def sitepackagesdir(envdir): from distutils.sysconfig import get_python_lib return dict(dir=get_python_lib(prefix=envdir)) diff --git a/tox/result.py b/tox/result.py index ee5ad09..7bedd3f 100644 --- a/tox/result.py +++ b/tox/result.py @@ -46,23 +46,24 @@ class EnvLog: def set_python_info(self, pythonexecutable): pythonexecutable = py.path.local(pythonexecutable) out = pythonexecutable.sysexec("-c", - "import sys; " - "print (sys.executable);" - "print (list(sys.version_info)); " - "print (sys.version)") + "import sys; " + "print (sys.executable);" + "print (list(sys.version_info)); " + "print (sys.version)") lines = out.splitlines() executable = lines.pop(0) version_info = eval(lines.pop(0)) version = "\n".join(lines) self.dict["python"] = dict( executable=executable, - version_info = version_info, - version = version) + version_info=version_info, + version=version) def get_commandlog(self, name): l = self.dict.setdefault(name, []) return CommandLog(self, l) + class CommandLog: def __init__(self, envlog, list): self.envlog = envlog |