Commits

Bruno Oliveira committed 93a8674

Added --force-dep-version command-line option

Comments (0)

Files changed (3)

 Lukasz Balcerzak 
 Philip Thiem
 Monty Taylor
+Bruno Oliveira

tests/test_config.py

         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([], "")
             "*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:
 
         """)
         envconfig = config.envconfigs["py26"]
         assert envconfig.commands[0] == ["some", r"hello\world"]
-
-
 import string
 import subprocess
 import textwrap
+import pkg_resources
 
 from tox.interpreters import Interpreters
 
         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
             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)
             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 = []
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.