diff --git a/src/octoprint/util/pip.py b/src/octoprint/util/pip.py index c58c5c02..6de4213e 100644 --- a/src/octoprint/util/pip.py +++ b/src/octoprint/util/pip.py @@ -24,9 +24,38 @@ class UnknownPip(Exception): pass class PipCaller(CommandlineCaller): - process_dependency_links = pkg_resources.parse_requirements("pip>=1.5") - no_use_wheel = pkg_resources.parse_requirements("pip==1.5.0") - broken = pkg_resources.parse_requirements("pip>=6.0.1,<=6.0.3") + process_dependency_links = pkg_resources.Requirement.parse("pip>=1.5") + no_use_wheel = pkg_resources.Requirement.parse("pip==1.5.0") + broken = pkg_resources.Requirement.parse("pip>=6.0.1,<=6.0.3") + + @classmethod + def clean_install_command(cls, args, pip_version, virtual_env, use_user, force_user): + logger = logging.getLogger(__name__) + args = list(args) + + # strip --process-dependency-links for versions that don't support it + if not pip_version in cls.process_dependency_links and "--process-dependency-links" in args: + logger.debug( + "Found --process-dependency-links flag, version {} doesn't need that yet though, removing.".format( + pip_version)) + args.remove("--process-dependency-links") + + # add --no-use-wheel for versions that otherwise break + if pip_version in cls.no_use_wheel and not "--no-use-wheel" in args: + logger.debug("Version {} needs --no-use-wheel to properly work.".format(pip_version)) + args.append("--no-use-wheel") + + # remove --user if it's present and a virtual env is detected + if "--user" in args: + if virtual_env or not site.ENABLE_USER_SITE: + logger.debug("Virtual environment detected, removing --user flag.") + args.remove("--user") + # otherwise add it if necessary + elif not virtual_env and site.ENABLE_USER_SITE and (use_user or force_user): + logger.debug("pip needs --user flag for installations.") + args.append("--user") + + return args def __init__(self, configured=None, ignore_cache=False, force_sudo=False, force_user=False): @@ -126,25 +155,8 @@ class PipCaller(CommandlineCaller): arg_list = list(args) if "install" in arg_list: - # strip --process-dependency-links for versions that don't support it - if not self.version in self.__class__.process_dependency_links and "--process-dependency-links" in arg_list: - self._logger.debug("Found --process-dependency-links flag, version {} doesn't need that yet though, removing.".format(self.version)) - arg_list.remove("--process-dependency-links") - - # add --no-use-wheel for versions that otherwise break - if self.version in self.__class__.no_use_wheel and not "--no-use-wheel" in arg_list: - self._logger.debug("Version {} needs --no-use-wheel to properly work.".format(self.version)) - arg_list.append("--no-use-wheel") - - # remove --user if it's present and a virtual env is detected - if "--user" in arg_list: - if self._virtual_env or not site.ENABLE_USER_SITE: - self._logger.debug("Virtual environment detected, removing --user flag.") - arg_list.remove("--user") - # otherwise add it if necessary - elif not self._virtual_env and site.ENABLE_USER_SITE and (self.use_user or self.force_user): - self._logger.debug("pip needs --user flag for installations.") - arg_list.append("--user") + arg_list = self.clean_install_command(arg_list, self.version, self._virtual_env, self.use_user, + self.force_user) # add args to command if isinstance(self._command, list): diff --git a/src/octoprint/util/version.py b/src/octoprint/util/version.py index a2317b58..3db67e15 100644 --- a/src/octoprint/util/version.py +++ b/src/octoprint/util/version.py @@ -74,7 +74,7 @@ def is_octoprint_compatible(*compatibility_entries, **kwargs): if not any(octo_compat.startswith(c) for c in ("<", "<=", "!=", "==", ">=", ">", "~=", "===")): octo_compat = ">={}".format(octo_compat) - s = next(pkg_resources.parse_requirements("OctoPrint" + octo_compat)) + s = pkg_resources.Requirement.parse("OctoPrint" + octo_compat) if octoprint_version in s: break except: diff --git a/tests/util/test_pip.py b/tests/util/test_pip.py new file mode 100644 index 00000000..5b91b01f --- /dev/null +++ b/tests/util/test_pip.py @@ -0,0 +1,80 @@ +# coding=utf-8 +from __future__ import absolute_import + +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2017 The OctoPrint Project - Released under terms of the AGPLv3 License" + +import unittest +import ddt +import mock + +import octoprint.util.pip + +import site +import pkg_resources + +@ddt.ddt +class PipCallerTest(unittest.TestCase): + + @ddt.data( + # remove --process-dependency-links for versions < 1.5 + (["install", "--process-dependency-links", "http://example.com/foo.zip"], "1.1", True, False, False, + True, + ["install", "http://example.com/foo.zip"]), + + # keep --process-dependency-links for versions >= 1.5, --no-use-wheel for ==1.5.0 + (["install", "--process-dependency-links", "http://example.com/foo.zip"], "1.5", True, False, False, + True, + ["install", "--process-dependency-links", "http://example.com/foo.zip", "--no-use-wheel"]), + + # keep --process-dependency-links for versions >= 1.5 + (["install", "--process-dependency-links", "http://example.com/foo.zip"], "9.0.1", True, False, False, + True, + ["install", "--process-dependency-links", "http://example.com/foo.zip"]), + + # remove --user in virtual env + (["install", "--user", "http://example.com/foo.zip"], "9.0.1", True, False, False, + True, + ["install", "http://example.com/foo.zip"]), + + # ignore use_user in virtual env + (["install", "http://example.com/foo.zip"], "9.0.1", True, True, False, + True, + ["install", "http://example.com/foo.zip"]), + + # ignore force_user in virtual env + (["install", "http://example.com/foo.zip"], "9.0.1", True, False, True, + True, + ["install", "http://example.com/foo.zip"]), + + # remove --user with disabled user_site + (["install", "--user", "http://example.com/foo.zip"], "9.0.1", False, False, False, + False, + ["install", "http://example.com/foo.zip"]), + + # add --user when not in virtual env and use_user is True + (["install", "http://example.com/foo.zip"], "9.0.1", False, True, False, + True, + ["install", "http://example.com/foo.zip", "--user"]), + + # ignore use_user with disabled user_site + (["install", "http://example.com/foo.zip"], "9.0.1", False, True, False, + False, + ["install", "http://example.com/foo.zip"]), + + # add --user when not in virtual env and force_user is True + (["install", "http://example.com/foo.zip"], "9.0.1", False, False, True, + True, + ["install", "http://example.com/foo.zip", "--user"]), + + # ignore force_user with disabled user_site + (["install", "http://example.com/foo.zip"], "9.0.1", False, False, True, + False, + ["install", "http://example.com/foo.zip"]), + ) + @ddt.unpack + def test_clean_install_command(self, args, version, virtual_env, use_user, force_user, user_site, expected): + with mock.patch.object(site, "ENABLE_USER_SITE", user_site): + parsed = pkg_resources.parse_version(version) + actual = octoprint.util.pip.PipCaller.clean_install_command(args, parsed, virtual_env, use_user, force_user) + self.assertEquals(expected, actual)