summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruno Oliveira <nicoddemus@gmail.com>2013-11-21 20:15:14 -0200
committerBruno Oliveira <nicoddemus@gmail.com>2013-11-21 20:15:14 -0200
commit8f1b64a35a51a81ee85f65d3a6776297169e135b (patch)
treeff09470b80dbaeed8b492d87ff4152305f0fce42
parente20548ba593c3726a93ac46d885d96eca4c77fce (diff)
downloadtox-8f1b64a35a51a81ee85f65d3a6776297169e135b.tar.gz
Added --force-dep-version command-line option
-rw-r--r--CONTRIBUTORS1
-rw-r--r--tests/test_config.py59
-rw-r--r--tox/_config.py31
3 files changed, 89 insertions, 2 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index c65d497..9d8784a 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -11,3 +11,4 @@ Ronny Pfannschmidt
Lukasz Balcerzak
Philip Thiem
Monty Taylor
+Bruno Oliveira
diff --git a/tests/test_config.py b/tests/test_config.py
index f07f191..517ec29 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -62,6 +62,41 @@ class TestVenvConfig:
envconfig = config.envconfigs['devenv']
assert envconfig.envdir == config.toxworkdir.join('foobar')
+ def test_force_dep_version(self, initproj):
+ """
+ Make sure we can override dependencies configured in tox.ini when using the command line option
+ --force-dep-version.
+ """
+ initproj("example123-0.5", filedefs={
+ 'tox.ini': '''
+ [tox]
+
+ [testenv]
+ deps=
+ dep1==1.0
+ dep2>=2.0
+ dep3
+ dep4==4.0
+ '''
+ })
+ config = parseconfig(
+ ['--force-dep-version=dep1==1.5', '--force-dep-version=dep2==2.1', '--force-dep-version=dep3==3.0'])
+ assert config.option.force_dep_version == ['dep1==1.5', 'dep2==2.1', 'dep3==3.0']
+ assert [str(x) for x in config.envconfigs['python'].deps] == [
+ 'dep1==1.5', 'dep2==2.1', 'dep3==3.0', 'dep4==4.0',
+ ]
+
+ def test_is_same_dep(self):
+ """
+ Ensure correct parseini._is_same_dep is working with a few samples.
+ """
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3>=2.0')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3>2.0')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3<2.0')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3<=2.0')
+ assert not parseini._is_same_dep('pkg_hello-world3==1.0', 'otherpkg>=2.0')
+
class TestConfigPackage:
def test_defaults(self, tmpdir, newconfig):
config = newconfig([], "")
@@ -1142,6 +1177,28 @@ class TestCmdInvocation:
"*ERROR*tox.ini*not*found*",
])
+ def test_showconfig_with_force_dep_version(self, cmd, initproj):
+ initproj('force_dep_version', filedefs={
+ 'tox.ini': '''
+ [tox]
+
+ [testenv]
+ deps=
+ dep1==2.3
+ dep2
+ ''',
+ })
+ result = cmd.run("tox", "--showconfig")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ r'*deps=*dep1==2.3, dep2*',
+ ])
+ # override dep1 specific version, and force version for dep2
+ result = cmd.run("tox", "--showconfig", "--force-dep-version=dep1", "--force-dep-version=dep2==5.0")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ r'*deps=*dep1, dep2==5.0*',
+ ])
class TestArgumentParser:
@@ -1229,5 +1286,3 @@ class TestCommandParser:
""")
envconfig = config.envconfigs["py26"]
assert envconfig.commands[0] == ["some", r"hello\world"]
-
-
diff --git a/tox/_config.py b/tox/_config.py
index e1cc966..0348080 100644
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -8,6 +8,7 @@ import shlex
import string
import subprocess
import textwrap
+import pkg_resources
from tox.interpreters import Interpreters
@@ -124,6 +125,10 @@ def prepare_parse(pkgname):
help="set PYTHONHASHSEED to SEED before running commands. "
"Defaults to a random integer in the range 1 to 4294967295. "
"Passing 'noset' suppresses this behavior.")
+ parser.add_argument("--force-dep-version", action="append",
+ metavar="DEP==VER", default=None,
+ help="Forces a certain version of one of the dependencies "
+ "when configuring the virtual environment.")
parser.add_argument("args", nargs="*",
help="additional arguments available to command positional substitution")
return parser
@@ -343,6 +348,7 @@ class parseini:
else:
name = depline.strip()
ixserver = None
+ name = self._replace_forced_dep(name, config)
vc.deps.append(DepConfig(name, ixserver))
vc.distribute = reader.getbool(section, "distribute", False)
vc.sitepackages = reader.getbool(section, "sitepackages", False)
@@ -385,6 +391,31 @@ class parseini:
envlist.sort()
return envlist
+ def _replace_forced_dep(self, name, config):
+ """
+ Override the given dependency config name taking --force-dep-version
+ option into account.
+
+ :param name: dep config, for example ["pkg==1.0", "other==2.0"].
+ :param config: Config instance
+ :return: the new dependency that should be used for virtual environments
+ """
+ if not config.option.force_dep_version:
+ return name
+ for forced_dep in config.option.force_dep_version:
+ if self._is_same_dep(forced_dep, name):
+ return forced_dep
+ return name
+
+ @classmethod
+ def _is_same_dep(cls, dep1, dep2):
+ """
+ Returns True if both dependency definitions refer to the same package, even if versions differ.
+ """
+ dep1_name = pkg_resources.Requirement.parse(dep1).project_name
+ dep2_name = pkg_resources.Requirement.parse(dep2).project_name
+ return dep1_name == dep2_name
+
def _split_env(env):
"""if handed a list, action="append" was used for -e """
envlist = []