Skip to content

[wip] Pep 518 #861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/850.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PEP-518 support: provide a tox configuration flag ``build`` which can be either ``sdist`` or ``wheel``. For ``sdist`` (default) we build the package as before by using ``python setup.py sdist``. However, when ``wheel`` is enabled now we'll use ``pip wheel`` to build it, and we'll also install wheels in these case into the environments. Note: ``pip`` 10 supports specifying project dependencies (such as ``setuptools-scm``, or a given ``setuptools`` version) via ``pyproject.toml``. Once ``pip`` supports building ``sdist`` to we'll migrate over the ``sdist`` build too.`@gaborbernat <https://github.com/gaborbernat>`_
1 change: 1 addition & 0 deletions changelog/851.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
While running tox invokes various commands (such as building the package, pip installing dependencies and so on), these were printed in case they failed as Python arrays. Changed the representation to a shell command, allowing the users to quickly replicate/debug the failure on their own.`@gaborbernat <https://github.com/gaborbernat>`_
2 changes: 0 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ def generate_draft_news():


def setup(app):
# from sphinx.ext.autodoc import cut_lines
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
app.add_object_type(
"confval",
"confval",
Expand Down
30 changes: 30 additions & 0 deletions doc/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,36 @@ and will first lookup global tox settings in this section:
is identified. In a future version of tox, this warning will become an
error.

.. confval:: build=sdist|wheel|none

.. versionadded:: 3.1.0

tox will try to build the project as a Python package and install it in a fresh virtual
environment before running your test commands. Here one can select the type of build
tox will do for the projects package:

- if it's ``none`` tox will not try to build the package (implies no install happens later),
- if it's ``sdist`` it will create a source distribution by using:

.. code-block:: ini

python setup.py sdist

- if it's ``wheel`` it will create a Python wheel by using ``pip``:

.. code-block:: ini

pip wheel . --no-dep

.. note::

wheel should only be used with pip ``10+`` and a ``pyproject.toml``, the build will
fail if either is missing


**Default:** ``sdist``



Virtualenv test environment settings
------------------------------------
Expand Down
10 changes: 6 additions & 4 deletions src/tox/_pytestplugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import print_function, unicode_literals

import os
import re
import textwrap
import time
from fnmatch import fnmatch
Expand Down Expand Up @@ -61,15 +62,12 @@ def create_new_config_file_(args, source=None, plugins=()):


@pytest.fixture
def cmd(request, capfd, monkeypatch):
def cmd(request, capfd):
if request.config.option.no_network:
pytest.skip("--no-network was specified, test cannot run")
request.addfinalizer(py.path.local().chdir)

def run(*argv):
key = str(b"PYTHONPATH")
python_paths = (i for i in (str(os.getcwd()), os.getenv(key)) if i)
monkeypatch.setenv(key, os.pathsep.join(python_paths))
with RunResult(capfd, argv) as result:
try:
main([str(x) for x in argv])
Expand Down Expand Up @@ -111,6 +109,10 @@ def __repr__(self):
self.ret, " ".join(str(i) for i in self.args), self.out, self.err
)

@property
def python_hash_seed(self):
return next(re.finditer(r"PYTHONHASHSEED='([0-9]+)'", self.out)).group(1)


class ReportExpectMock:
def __init__(self, session):
Expand Down
1 change: 1 addition & 0 deletions src/tox/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,7 @@ def __init__(self, config, inipath):
for name in config.indexserver:
config.indexserver[name] = IndexServerConfig(name, override)

config.build = reader.getstring("build", default="sdist")
reader.addsubstitutions(toxworkdir=config.toxworkdir)
config.distdir = reader.getpath("distdir", "{toxworkdir}/dist")
reader.addsubstitutions(distdir=config.distdir)
Expand Down
88 changes: 64 additions & 24 deletions src/tox/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
from __future__ import print_function

import os
import pipes
import re
import shutil
import subprocess
import sys
import time

import py
from packaging.version import InvalidVersion, Version
from packaging.version import InvalidVersion, Version, parse
from pkg_resources import get_distribution

import tox
from tox.config import parseconfig
Expand Down Expand Up @@ -136,9 +138,16 @@ def _initlogpath(self, actionid):
def popen(self, args, cwd=None, env=None, redirect=True, returnout=False, ignore_ret=False):
stdout = outpath = None
resultjson = self.session.config.option.resultjson

cmd_args = [str(x) for x in args]
cmd_args_shell = " ".join(pipes.quote(i) for i in cmd_args)
if resultjson or redirect:
fout = self._initlogpath(self.id)
fout.write("actionid: {}\nmsg: {}\ncmdargs: {!r}\n\n".format(self.id, self.msg, args))
fout.write(
"actionid: {}\nmsg: {}\ncmdargs: {!r}\n\n".format(
self.id, self.msg, cmd_args_shell
)
)
fout.flush()
outpath = py.path.local(fout.name)
fin = outpath.open("rb")
Expand All @@ -153,11 +162,13 @@ def popen(self, args, cwd=None, env=None, redirect=True, returnout=False, ignore
popen = self._popen(args, cwd, env=env, stdout=stdout, stderr=subprocess.STDOUT)
except OSError as e:
self.report.error(
"invocation failed (errno {:d}), args: {}, cwd: {}".format(e.errno, args, cwd)
"invocation failed (errno {:d}), args: {}, cwd: {}".format(
e.errno, cmd_args_shell, cwd
)
)
raise
popen.outpath = outpath
popen.args = [str(x) for x in args]
popen.args = cmd_args
popen.cwd = cwd
popen.action = self
self._popenlist.append(popen)
Expand Down Expand Up @@ -431,7 +442,7 @@ def _copyfiles(self, srcdir, pathlist, destdir):
target.dirpath().ensure(dir=1)
src.copy(target)

def _makesdist(self):
def _get_package(self):
setup = self.config.setupdir.join("setup.py")
if not setup.check():
self.report.error(
Expand All @@ -446,19 +457,8 @@ def _makesdist(self):
)
raise SystemExit(1)
with self.newaction(None, "packaging") as 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,
)
action.setactivity("{}-make".format(self.config.build), setup)
self._make_package(action, setup)
try:
return self.config.distdir.listdir()[0]
except py.error.ENOENT:
Expand All @@ -478,6 +478,46 @@ def _makesdist(self):
)
raise SystemExit(1)

def _make_package(self, action, setup):
self.make_emptydir(self.config.distdir)
if self.config.build == "sdist":
action.popen(
[
sys.executable,
setup,
"sdist",
"--formats=zip",
"--dist-dir",
self.config.distdir,
],
cwd=self.config.setupdir,
)
elif self.config.build == "wheel":
if parse(get_distribution("pip").version).release[0] > 10:
raise RuntimeError("wheel support requires pip 10 or later")
py_project_toml = self.config.setupdir.join("pyproject.toml")
if not py_project_toml.exists():
raise RuntimeError(
"wheel support requires creating and setting build-requires in {}".format(
py_project_toml
)
)
action.popen(
[
sys.executable,
"-m",
"pip",
"wheel",
"-w",
self.config.distdir, # target folder
"--no-deps", # don't build wheels for package dependencies
self.config.setupdir, # what to build - ourselves
],
cwd=self.config.setupdir,
)
else:
raise RuntimeError("invalid build type {}".format(self.config.build))

def make_emptydir(self, path):
if path.check():
self.report.info(" removing {}".format(path))
Expand Down Expand Up @@ -576,23 +616,23 @@ def get_installpkg_path(self):
if not path:
path = self.config.sdistsrc
path = self._resolve_package(path)
self.report.info("using package {!r}, skipping 'sdist' activity ".format(str(path)))
self.report.info(
"using package {!r}, skipping '{}' activity ".format(str(path), self.config.build)
)
else:
try:
path = self._makesdist()
path = self._get_package()
except tox.exception.InvocationError:
v = sys.exc_info()[1]
self.report.error("FAIL could not package project - v = {!r}".format(v))
return
sdistfile = self.config.distshare.join(path.basename)
if sdistfile != path:
self.report.info("copying new sdistfile to {!r}".format(str(sdistfile)))
self.report.info("copying new package to {!r}".format(str(sdistfile)))
try:
sdistfile.dirpath().ensure(dir=1)
except py.error.Error:
self.report.warning(
"could not copy distfile to {}".format(sdistfile.dirpath())
)
self.report.warning("could not copy package to {}".format(sdistfile.dirpath()))
else:
path.copy(sdistfile)
return path
Expand Down
17 changes: 15 additions & 2 deletions src/tox/venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,15 @@ def test(self, redirect=False):
raise

def _pcall(
self, args, cwd, venv=True, testcommand=False, action=None, redirect=True, ignore_ret=False
self,
args,
cwd,
venv=True,
testcommand=False,
action=None,
redirect=True,
ignore_ret=False,
no_python_path=False,
):
os.environ.pop("VIRTUALENV_PYTHON", None)

Expand All @@ -423,6 +431,8 @@ def _pcall(
env = self._getenv(testcommand=testcommand)
bindir = str(self.envconfig.envbindir)
env["PATH"] = p = os.pathsep.join([bindir, os.environ["PATH"]])
if no_python_path:
env.pop("PYTHONPATH", None)
self.session.report.verbosity2("setting PATH={}".format(p))
return action.popen(args, cwd=cwd, env=env, redirect=redirect, ignore_ret=ignore_ret)

Expand Down Expand Up @@ -496,7 +506,10 @@ def tox_runtest(venv, redirect):
def tox_runenvreport(venv, action):
# write out version dependency information
args = venv.envconfig.list_dependencies_command
output = venv._pcall(args, cwd=venv.envconfig.config.toxinidir, action=action)
# we clear the PYTHONPATH to allow reporting packages outside of this environment
output = venv._pcall(
args, cwd=venv.envconfig.config.toxinidir, action=action, no_python_path=True
)
# the output contains a mime-header, skip it
output = output.split("\n\n")[-1]
packages = output.strip().split("\n")
Expand Down
Loading