Skip to content

Commit

Permalink
Add the global venvsdist option that will run the project's `./setu…
Browse files Browse the repository at this point in the history
…p.py sdist` command sandboxed into a `virtualenv`
  • Loading branch information
ntninja committed Aug 14, 2017
1 parent a9c7135 commit 245da5b
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 32 deletions.
14 changes: 11 additions & 3 deletions tox/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,12 @@ def __init__(self, config, inipath):
for env in _split_env(stated_envlist):
known_factors.update(env.split('-'))

# sdist creation environment
config.sdist_envconfig = self.make_envconfig(
"sdist", "toxenv:sdist", reader._subs, config, replace=True,
fallbacksections=[]
)

# configure testenvs
for name in all_envs:
section = testenvprefix + name
Expand All @@ -824,6 +830,7 @@ def __init__(self, config, inipath):
for name in config.envlist)

config.skipsdist = reader.getbool("skipsdist", all_develop)
config.venvsdist = reader.getbool("venvsdist", False)

def _list_section_factors(self, section):
factors = set()
Expand All @@ -833,10 +840,11 @@ def _list_section_factors(self, section):
factors.update(*mapcat(_split_factor_expr, exprs))
return factors

def make_envconfig(self, name, section, subs, config, replace=True):
def make_envconfig(self, name, section, subs, config, replace=True,
fallbacksections=["testenv"]):
factors = set(name.split('-'))
reader = SectionReader(section, self._cfg, fallbacksections=["testenv"],
factors=factors)
reader = SectionReader(section, self._cfg, factors=factors,
fallbacksections=fallbacksections)
vc = TestenvConfig(config=config, envname=name, factors=factors, reader=reader)
reader.addsubstitutions(**subs)
reader.addsubstitutions(envname=name)
Expand Down
97 changes: 73 additions & 24 deletions tox/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,9 @@ def __init__(self, config, popen=subprocess.Popen, Report=Reporter):
def hook(self):
return self.config.pluginmanager.hook

def _makevenv(self, name):
envconfig = self.config.envconfigs.get(name, None)
def _makevenv(self, name, envconfig=None):
if envconfig is None:
envconfig = self.config.envconfigs.get(name, None)
if envconfig is None:
self.report.error("unknown environment %r" % name)
raise LookupError(name)
Expand Down Expand Up @@ -401,19 +402,54 @@ def _copyfiles(self, srcdir, pathlist, destdir):
target.dirpath().ensure(dir=1)
src.copy(target)

def _makesdistvenv(self, setup_cmd, cwd):
envconfig = self.config.sdist_envconfig
try:
envconfig.changedir = cwd
envconfig.commands.append([str(envconfig.envpython)] + setup_cmd)
envconfig.list_dependencies_command = ["pip", "freeze", "--all"]

return self._makevenv("sdist", envconfig)
except LookupError:
raise SystemExit(1)
except tox.exception.ConfigError as e:
self.report.error(str(e))
raise SystemExit(1)

def _makesdist(self):
setup = self.config.setupdir.join("setup.py")
if not setup.check():
raise tox.exception.MissingFile(setup)
setup_cmd = [setup, "sdist", "--formats=zip",
"--dist-dir", self.config.distdir]

if self.config.venvsdist:
venv_sdist = self._makesdistvenv(setup_cmd, cwd=self.config.setupdir)
if not self.setupenv(venv_sdist, log_context=None):
raise SystemExit(1)
# write out version dependency information
self.envreport(venv_sdist, log_context=None)

action = self.newaction(None, "packaging")
with action:
action.setactivity("sdist-make", setup)
action.setactivity("sdist-inst", setup)
self.make_emptydir(self.config.distdir)
action.popen([sys.executable, setup, "sdist", "--formats=zip",
"--dist-dir", self.config.distdir, ],
cwd=self.config.setupdir)

if self.config.venvsdist:
if not venv_sdist.test(redirect=True, log_context=None, log_stage="sdist-make"):
raise SystemExit(1)
else:
action.popen([sys.executable, setup, "sdist", "--formats=zip",
"--dist-dir", self.config.distdir],
cwd=self.config.setupdir)

try:
return self.config.distdir.listdir()[0]
filelist = self.config.distdir.listdir()[0]

if self.config.venvsdist:
self.finishvenv(venv_sdist, log_context=None)

return filelist
except py.error.ENOENT:
# check if empty or comment only
data = []
Expand All @@ -439,17 +475,20 @@ def make_emptydir(self, path):
py.std.shutil.rmtree(str(path), ignore_errors=True)
path.ensure(dir=1)

def setupenv(self, venv):
def setupenv(self, venv, log_context=0):
if log_context == 0:
log_context = 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)
action = self.newaction(log_context, "getenv", venv.envconfig.envdir)
with action:
venv.status = 0
envlog = self.resultlog.get_envlog(venv.name)
try:
status = venv.update(action=action)
except FileNotFoundError as e:
except py.error.ENOENT as e:
status = (
"Error creating virtualenv. "
"Note that spaces in path are not supported by virtualenv. "
Expand All @@ -472,8 +511,11 @@ def setupenv(self, venv):
envlog.set_python_info(commandpath)
return True

def finishvenv(self, venv):
action = self.newaction(venv, "finishvenv")
def finishvenv(self, venv, log_context=0):
if log_context == 0:
log_context = venv

action = self.newaction(log_context, "finishvenv")
with action:
venv.finish()
return True
Expand All @@ -488,6 +530,24 @@ def developpkg(self, venv, setupdir):
venv.status = sys.exc_info()[1]
return False

def envreport(self, venv, log_context=0):
if log_context == 0:
log_context = venv

# write out version dependency information
action = self.newaction(log_context, "envreport")
with action:
args = venv.envconfig.list_dependencies_command
output = venv._pcall(args,
cwd=self.config.toxinidir,
action=action)
# the output contains a environmental 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)

def installpkg(self, venv, path):
"""Install package in the specified virtual environment.
Expand Down Expand Up @@ -560,18 +620,7 @@ def subcommand_test(self):
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.envreport(venv)

self.runtestenv(venv)
retcode = self._summary()
Expand Down
17 changes: 12 additions & 5 deletions tox/venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def update(self, action):
self.envconfig.deps, v)

def _getliveconfig(self):
python = self.envconfig.python_info.executable
python = str(self.envconfig.python_info.executable)
md5 = getdigest(python)
version = tox.__version__
sitepackages = self.envconfig.sitepackages
Expand Down Expand Up @@ -358,22 +358,26 @@ def _getenv(self, testcommand=False):
env['VIRTUAL_ENV'] = str(self.path)
return env

def test(self, redirect=False):
action = self.session.newaction(self, "runtests")
def test(self, redirect=False, log_context=0, log_stage="runtests"):
if log_context == 0:
log_context = self

success = True
action = self.session.newaction(log_context, log_stage)
with action:
self.status = 0
self.session.make_emptydir(self.envconfig.envtmpdir)
self.envconfig.envtmpdir.ensure(dir=1)
cwd = self.envconfig.changedir
env = self._getenv(testcommand=True)
# Display PYTHONHASHSEED to assist with reproducibility.
action.setactivity("runtests", "PYTHONHASHSEED=%r" % env.get('PYTHONHASHSEED'))
action.setactivity(log_stage, "PYTHONHASHSEED=%r" % env.get('PYTHONHASHSEED'))
for i, argv in enumerate(self.envconfig.commands):
# have to make strings as _pcall changes argv[0] to a local()
# happens if the same environment is invoked twice
message = "commands[%s] | %s" % (i, ' '.join(
[str(x) for x in argv]))
action.setactivity("runtests", message)
action.setactivity(log_stage, message)
# check to see if we need to ignore the return code
# if so, we need to alter the command line arguments
if argv[0].startswith("-"):
Expand All @@ -396,6 +400,7 @@ def test(self, redirect=False):
self.status = "ignored failed command"
continue # keep processing commands

success = False
self.session.report.error(str(err))
self.status = "commands failed"
if not self.envconfig.ignore_errors:
Expand All @@ -405,6 +410,8 @@ def test(self, redirect=False):
self.session.report.error(self.status)
raise

return success

def _pcall(self, args, cwd, venv=True, testcommand=False,
action=None, redirect=True, ignore_ret=False):
for name in ("VIRTUALENV_PYTHON", "PYTHONDONTWRITEBYTECODE"):
Expand Down

0 comments on commit 245da5b

Please sign in to comment.