Skip to content
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

[WIP] "requires" config improvements #1022

Merged
merged 5 commits into from
Sep 27, 2018
Merged
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
8 changes: 6 additions & 2 deletions doc/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,12 @@ Global settings are defined under the ``tox`` section as:
.. code-block:: ini

[tox]
requires = setuptools >= 30.0.0
py
requires = tox-venv
setuptools >= 30.0.0

.. note:: tox does **not** install those required packages for you. tox only checks if the
requirements are satisfied and crashes early with an helpful error rather then later
in the process.

.. conf:: isolated_build ^ true|false ^ false

Expand Down
13 changes: 7 additions & 6 deletions src/tox/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1084,19 +1084,20 @@ def _make_thread_safe_path(self, config, attr, unique_id):

@staticmethod
def ensure_requires_satisfied(specified):
fail = False
missing_requirements = []
for s in specified:
try:
pkg_resources.get_distribution(s)
except pkg_resources.RequirementParseError:
raise
except Exception:
fail = True
print(
"requirement missing {}".format(pkg_resources.Requirement(s)), file=sys.stderr
missing_requirements.append(str(pkg_resources.Requirement(s)))
if missing_requirements:
raise tox.exception.MissingRequirement(
"Packages {} need to be installed alongside tox in {}".format(
", ".join(missing_requirements), sys.executable
)
if fail:
raise RuntimeError("not all requirements satisfied, install them alongside tox")
)

def _list_section_factors(self, section):
factors = set()
Expand Down
4 changes: 4 additions & 0 deletions src/tox/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ class MissingDependency(Error):
"""A dependency could not be found or determined."""


class MissingRequirement(Error):
"""A requirement defined in :config:`require` is not met."""


class MinVersionError(Error):
"""The installed tox version is lower than requested minversion."""

Expand Down
1 change: 1 addition & 0 deletions src/tox/interpreters.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ def tox_get_python_executable(envconfig):
# The standard names are in predictable places.
actual = r"c:\python{}\python.exe".format("".join(groups))
else:

actual = win32map.get(name, None)
if actual:
actual = py.path.local(actual)
Expand Down
2 changes: 1 addition & 1 deletion src/tox/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def main(args):
raise SystemExit(retcode)
except KeyboardInterrupt:
raise SystemExit(2)
except tox.exception.MinVersionError as e:
except (tox.exception.MinVersionError, tox.exception.MissingRequirement) as e:
r = Reporter(None)
r.error(str(e))
raise SystemExit(1)
Expand Down
16 changes: 5 additions & 11 deletions tests/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2644,26 +2644,20 @@ def test_commands_with_backslash(self, newconfig):
assert envconfig.commands[0] == ["some", r"hello\world"]


def test_plugin_require(newconfig, capsys):
def test_plugin_require(newconfig):
inisource = """
[tox]
requires = tox
name[foo,bar]>=2,<3; python_version>"2.0" and os_name=='a'
b
"""
with pytest.raises(
RuntimeError, match="not all requirements satisfied, install them alongside tox"
):
with pytest.raises(tox.exception.MissingRequirement) as exc_info:
newconfig([], inisource)

out, err = capsys.readouterr()
assert err.strip() == "\n".join(
[
'requirement missing name[bar,foo]<3,>=2; python_version > "2.0" and os_name == "a"',
"requirement missing b",
]
assert exc_info.value.args[0] == (
r'Packages name[bar,foo]<3,>=2; python_version > "2.0" and os_name == "a", b '
r"need to be installed alongside tox in {}".format(sys.executable)
)
assert not out


def test_isolated_build_env_cannot_be_in_envlist(newconfig, capsys):
Expand Down
15 changes: 9 additions & 6 deletions tests/unit/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest

import tox
from tox.config import parseconfig

INI_BLOCK_RE = re.compile(
Expand All @@ -20,9 +21,9 @@
RST_FILES = []
TOX_ROOT = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
for root, _, filenames in os.walk(os.path.join(TOX_ROOT, "doc")):
for f in filenames:
if f.endswith(".rst"):
RST_FILES.append(os.path.join(root, f))
for filename in filenames:
if filename.endswith(".rst"):
RST_FILES.append(os.path.join(root, filename))


def test_some_files_exist():
Expand All @@ -35,10 +36,12 @@ def test_all_rst_ini_blocks_parse(filename, tmpdir):
contents = f.read()
for match in INI_BLOCK_RE.finditer(contents):
code = textwrap.dedent(match.group("code"))
f = tmpdir.join("tox.ini")
f.write(code)
config_path = tmpdir / "tox.ini"
config_path.write(code)
try:
parseconfig(("-c", str(f)))
parseconfig(["-c", str(config_path)])
except tox.exception.MissingRequirement:
assert "requires = tox-venv" in str(code)
except Exception as e:
raise AssertionError(
"Error parsing ini block\n\n"
Expand Down