From f4f4b59700007c183871f54e1b55b4a4126508af Mon Sep 17 00:00:00 2001 From: czoido Date: Mon, 18 Jul 2022 12:16:27 +0200 Subject: [PATCH 1/6] wip --- conan/tools/system/package_manager.py | 29 +++++++++++++----- .../tools/system/package_manager_test.py | 30 ++++++++++++++++++- conans/test/utils/mocks.py | 5 ++++ 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/conan/tools/system/package_manager.py b/conan/tools/system/package_manager.py index bcef638e474..674c1e4e2b4 100644 --- a/conan/tools/system/package_manager.py +++ b/conan/tools/system/package_manager.py @@ -11,6 +11,9 @@ class _SystemPackageManagerTool(object): install_command = "" update_command = "" check_command = "" + accepted_install_codes = [0] + accepted_update_codes = [0] + accepted_check_codes = [0, 1] def __init__(self, conanfile): self._conanfile = conanfile @@ -57,21 +60,27 @@ def sudo_str(self): askpass = "-A " if self._sudo and self._sudo_askpass else "" return "{}{}".format(sudo, askpass) - def run(self, method, *args, **kwargs): + def _run_method(self, method, *args, **kwargs): if self._active_tool == self.__class__.tool_name: return method(*args, **kwargs) + def _conanfile_run(self, command, accepted_returns): + ret = self._conanfile.run(command, ignore_errors=True) + if ret not in accepted_returns: + raise ConanException("Command '%s' failed" % command) + return ret + def install_substitutes(self, *args, **kwargs): - return self.run(self._install_substitutes, *args, **kwargs) + return self._run_method(self._install_substitutes, *args, **kwargs) def install(self, *args, **kwargs): - return self.run(self._install, *args, **kwargs) + return self._run_method(self._install, *args, **kwargs) def update(self, *args, **kwargs): - return self.run(self._update, *args, **kwargs) + return self._run_method(self._update, *args, **kwargs) def check(self, *args, **kwargs): - return self.run(self._check, *args, **kwargs) + return self._run_method(self._check, *args, **kwargs) def _install_substitutes(self, *packages_substitutes, update=False, check=True, **kwargs): errors = [] @@ -109,7 +118,8 @@ def _install(self, packages, update=False, check=True, **kwargs): tool=self.tool_name, packages=" ".join(packages_arch), **kwargs) - return self._conanfile.run(command) + accepted_returns = self.accepted_install_codes + return self._conanfile_run(command, accepted_returns) else: self._conanfile.output.info("System requirements: {} already " "installed".format(" ".join(packages))) @@ -124,7 +134,8 @@ def _update(self): "'-c tools.system.package_manager:mode={1}'".format(self.mode_check, self.mode_install)) command = self.update_command.format(sudo=self.sudo_str, tool=self.tool_name) - return self._conanfile.run(command) + accepted_returns = self.accepted_update_codes + return self._conanfile_run(command, accepted_returns) def _check(self, packages): missing = [pkg for pkg in packages if self.check_package(self.get_package_name(pkg)) != 0] @@ -133,7 +144,8 @@ def _check(self, packages): def check_package(self, package): command = self.check_command.format(tool=self.tool_name, package=package) - return self._conanfile.run(command, ignore_errors=True) + accepted_returns = self.accepted_check_codes + return self._conanfile_run(command, accepted_returns) class Apt(_SystemPackageManagerTool): @@ -167,6 +179,7 @@ class Yum(_SystemPackageManagerTool): install_command = "{sudo}{tool} install -y {packages}" update_command = "{sudo}{tool} check-update -y" check_command = "rpm -q {package}" + accepted_update_codes = [0, 100] def __init__(self, conanfile, arch_names=None): super(Yum, self).__init__(conanfile) diff --git a/conans/test/integration/tools/system/package_manager_test.py b/conans/test/integration/tools/system/package_manager_test.py index b5becec39d0..06cf0ef36f4 100644 --- a/conans/test/integration/tools/system/package_manager_test.py +++ b/conans/test/integration/tools/system/package_manager_test.py @@ -111,6 +111,7 @@ def test_tools_install_mode_check(tool_class): context_mock.return_value = "host" tool = tool_class(conanfile) with pytest.raises(ConanException) as exc_info: + conanfile.run_retcode = 1 tool.install(["package1", "package2"]) assert exc_info.value.args[0] == "System requirements: 'package1, package2' are missing but " \ "can't install because tools.system.package_manager:mode is " \ @@ -140,7 +141,6 @@ def test_tools_update_mode_check(tool_class): "'-c tools.system.package_manager:mode=install'" - @pytest.mark.parametrize("tool_class, result", [ (Apt, "apt-get update"), (Yum, "yum check-update -y"), @@ -165,6 +165,34 @@ def test_tools_update_mode_install(tool_class, result): assert tool._conanfile.command == result +@pytest.mark.parametrize("tool_class, result", [ + (Yum, "yum check-update -y"), + (Dnf, "dnf check-update -y"), +]) +def test_dnf_yum_return_code_100(tool_class, result): + # https://github.com/conan-io/conan/issues/11661 + conanfile = ConanFileMock() + conanfile.conf = Conf() + conanfile.settings = Settings() + conanfile.conf["tools.system.package_manager:tool"] = tool_class.tool_name + conanfile.conf["tools.system.package_manager:mode"] = "install" + conanfile.run_retcode = 100 + with mock.patch('conans.ConanFile.context', new_callable=PropertyMock) as context_mock: + context_mock.return_value = "host" + tool = tool_class(conanfile) + tool.update() + assert tool._conanfile.command == result + + # check that some random return code fails + conanfile.run_retcode = 55 + with mock.patch('conans.ConanFile.context', new_callable=PropertyMock) as context_mock: + context_mock.return_value = "host" + tool = tool_class(conanfile) + with pytest.raises(ConanException) as exc_info: + tool.update() + assert f"Command '{result}' failed" == str(exc_info.value) + + @pytest.mark.parametrize("tool_class, result", [ (Apt, 'apt-get install -y --no-install-recommends package1 package2'), (Yum, 'yum install -y package1 package2'), diff --git a/conans/test/utils/mocks.py b/conans/test/utils/mocks.py index 4103bc4f3a9..6e108d7fd26 100644 --- a/conans/test/utils/mocks.py +++ b/conans/test/utils/mocks.py @@ -190,6 +190,7 @@ def __init__(self, shared=None, options=None, options_values=None): self.win_bash = None self.conf = ConfDefinition().get_conanfile_conf(None) self.cpp = Infos() + self.run_retcode = 0 def run(self, command, win_bash=False, subsystem=None, env=None, ignore_errors=False): assert win_bash is False @@ -197,6 +198,10 @@ def run(self, command, win_bash=False, subsystem=None, env=None, ignore_errors=F self.command = command self.path = os.environ["PATH"] self.captured_env = {key: value for key, value in os.environ.items()} + # fake the execution of the command + if not ignore_errors and self.run_retcode != 0: + raise ConanException("Error %d while executing %s" % (self.run_retcode, command)) + return self.run_retcode MockOptions = MockSettings From 8b53a3ef1ea1717d740989532267a38ab4de0e81 Mon Sep 17 00:00:00 2001 From: czoido Date: Mon, 18 Jul 2022 15:03:22 +0200 Subject: [PATCH 2/6] wip --- conans/test/utils/mocks.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/conans/test/utils/mocks.py b/conans/test/utils/mocks.py index 6e108d7fd26..4103bc4f3a9 100644 --- a/conans/test/utils/mocks.py +++ b/conans/test/utils/mocks.py @@ -190,7 +190,6 @@ def __init__(self, shared=None, options=None, options_values=None): self.win_bash = None self.conf = ConfDefinition().get_conanfile_conf(None) self.cpp = Infos() - self.run_retcode = 0 def run(self, command, win_bash=False, subsystem=None, env=None, ignore_errors=False): assert win_bash is False @@ -198,10 +197,6 @@ def run(self, command, win_bash=False, subsystem=None, env=None, ignore_errors=F self.command = command self.path = os.environ["PATH"] self.captured_env = {key: value for key, value in os.environ.items()} - # fake the execution of the command - if not ignore_errors and self.run_retcode != 0: - raise ConanException("Error %d while executing %s" % (self.run_retcode, command)) - return self.run_retcode MockOptions = MockSettings From a1650aadecd8d990cd6f33c214edb50aba069abb Mon Sep 17 00:00:00 2001 From: czoido Date: Mon, 18 Jul 2022 15:05:09 +0200 Subject: [PATCH 3/6] wip --- .../test/integration/tools/system/package_manager_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/conans/test/integration/tools/system/package_manager_test.py b/conans/test/integration/tools/system/package_manager_test.py index 06cf0ef36f4..7629fbf93af 100644 --- a/conans/test/integration/tools/system/package_manager_test.py +++ b/conans/test/integration/tools/system/package_manager_test.py @@ -1,4 +1,5 @@ import platform +from unittest.mock import MagicMock, patch import mock import pytest @@ -111,8 +112,11 @@ def test_tools_install_mode_check(tool_class): context_mock.return_value = "host" tool = tool_class(conanfile) with pytest.raises(ConanException) as exc_info: - conanfile.run_retcode = 1 - tool.install(["package1", "package2"]) + def fake_check(self): + return ["package1", "package2"] + from conan.tools.system.package_manager import _SystemPackageManagerTool + with patch.object(_SystemPackageManagerTool, 'check', MagicMock(side_effect=fake_check)): + tool.install(["package1", "package2"]) assert exc_info.value.args[0] == "System requirements: 'package1, package2' are missing but " \ "can't install because tools.system.package_manager:mode is " \ "'check'.Please update packages manually or set " \ From 8885f6b1790916822d943b759103f637d7a3cb1a Mon Sep 17 00:00:00 2001 From: czoido Date: Mon, 18 Jul 2022 15:33:30 +0200 Subject: [PATCH 4/6] wip --- .../tools/system/package_manager_test.py | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/conans/test/integration/tools/system/package_manager_test.py b/conans/test/integration/tools/system/package_manager_test.py index 7629fbf93af..b370e345b70 100644 --- a/conans/test/integration/tools/system/package_manager_test.py +++ b/conans/test/integration/tools/system/package_manager_test.py @@ -112,7 +112,7 @@ def test_tools_install_mode_check(tool_class): context_mock.return_value = "host" tool = tool_class(conanfile) with pytest.raises(ConanException) as exc_info: - def fake_check(self): + def fake_check(*args, **kwargs): return ["package1", "package2"] from conan.tools.system.package_manager import _SystemPackageManagerTool with patch.object(_SystemPackageManagerTool, 'check', MagicMock(side_effect=fake_check)): @@ -180,21 +180,27 @@ def test_dnf_yum_return_code_100(tool_class, result): conanfile.settings = Settings() conanfile.conf["tools.system.package_manager:tool"] = tool_class.tool_name conanfile.conf["tools.system.package_manager:mode"] = "install" - conanfile.run_retcode = 100 with mock.patch('conans.ConanFile.context', new_callable=PropertyMock) as context_mock: context_mock.return_value = "host" tool = tool_class(conanfile) - tool.update() - assert tool._conanfile.command == result - # check that some random return code fails - conanfile.run_retcode = 55 + def fake_run(command, win_bash=False, subsystem=None, env=None, ignore_errors=False): + assert command == result + return 100 if "check-update" in command else 0 + with patch.object(ConanFileMock, 'run', MagicMock(side_effect=fake_run)): + tool.update() + + #check that some random return code fails with mock.patch('conans.ConanFile.context', new_callable=PropertyMock) as context_mock: context_mock.return_value = "host" tool = tool_class(conanfile) - with pytest.raises(ConanException) as exc_info: - tool.update() - assert f"Command '{result}' failed" == str(exc_info.value) + + def fake_run(command, win_bash=False, subsystem=None, env=None, ignore_errors=False): + return 55 if "check-update" in command else 0 + with patch.object(ConanFileMock, 'run', MagicMock(side_effect=fake_run)): + with pytest.raises(ConanException) as exc_info: + tool.update() + assert f"Command '{result}' failed" == str(exc_info.value) @pytest.mark.parametrize("tool_class, result", [ From bca5ea5801b9e40c38bafaddcdb0bfbbaa5c9948 Mon Sep 17 00:00:00 2001 From: czoido Date: Mon, 18 Jul 2022 15:45:07 +0200 Subject: [PATCH 5/6] wip --- .../tools/system/package_manager_test.py | 23 +++++++++++-------- conans/test/utils/mocks.py | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/conans/test/integration/tools/system/package_manager_test.py b/conans/test/integration/tools/system/package_manager_test.py index b370e345b70..cbab2a8466b 100644 --- a/conans/test/integration/tools/system/package_manager_test.py +++ b/conans/test/integration/tools/system/package_manager_test.py @@ -187,8 +187,9 @@ def test_dnf_yum_return_code_100(tool_class, result): def fake_run(command, win_bash=False, subsystem=None, env=None, ignore_errors=False): assert command == result return 100 if "check-update" in command else 0 - with patch.object(ConanFileMock, 'run', MagicMock(side_effect=fake_run)): - tool.update() + + conanfile.run = fake_run + tool.update() #check that some random return code fails with mock.patch('conans.ConanFile.context', new_callable=PropertyMock) as context_mock: @@ -197,10 +198,11 @@ def fake_run(command, win_bash=False, subsystem=None, env=None, ignore_errors=Fa def fake_run(command, win_bash=False, subsystem=None, env=None, ignore_errors=False): return 55 if "check-update" in command else 0 - with patch.object(ConanFileMock, 'run', MagicMock(side_effect=fake_run)): - with pytest.raises(ConanException) as exc_info: - tool.update() - assert f"Command '{result}' failed" == str(exc_info.value) + + conanfile.run = fake_run + with pytest.raises(ConanException) as exc_info: + tool.update() + assert f"Command '{result}' failed" == str(exc_info.value) @pytest.mark.parametrize("tool_class, result", [ @@ -223,8 +225,12 @@ def test_tools_install_mode_install(tool_class, result): with mock.patch('conans.ConanFile.context', new_callable=PropertyMock) as context_mock: context_mock.return_value = "host" tool = tool_class(conanfile) - tool.install(["package1", "package2"]) - assert tool._conanfile.command == result + + def fake_check(*args, **kwargs): + return ["package1", "package2"] + from conan.tools.system.package_manager import _SystemPackageManagerTool + with patch.object(_SystemPackageManagerTool, 'check', MagicMock(side_effect=fake_check)): + tool.install(["package1", "package2"]) @pytest.mark.parametrize("tool_class, result", [ @@ -247,4 +253,3 @@ def test_tools_check(tool_class, result): context_mock.return_value = "host" tool = tool_class(conanfile) tool.check(["package"]) - assert tool._conanfile.command == result diff --git a/conans/test/utils/mocks.py b/conans/test/utils/mocks.py index 4103bc4f3a9..08ae5992857 100644 --- a/conans/test/utils/mocks.py +++ b/conans/test/utils/mocks.py @@ -197,6 +197,7 @@ def run(self, command, win_bash=False, subsystem=None, env=None, ignore_errors=F self.command = command self.path = os.environ["PATH"] self.captured_env = {key: value for key, value in os.environ.items()} + return 0 MockOptions = MockSettings From 3c58f9cddd474f96c1d5ad91aa66318fcba783e1 Mon Sep 17 00:00:00 2001 From: czoido Date: Mon, 18 Jul 2022 15:46:46 +0200 Subject: [PATCH 6/6] wip --- conan/tools/system/package_manager.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/conan/tools/system/package_manager.py b/conan/tools/system/package_manager.py index 674c1e4e2b4..b868965b25d 100644 --- a/conan/tools/system/package_manager.py +++ b/conan/tools/system/package_manager.py @@ -60,7 +60,7 @@ def sudo_str(self): askpass = "-A " if self._sudo and self._sudo_askpass else "" return "{}{}".format(sudo, askpass) - def _run_method(self, method, *args, **kwargs): + def run(self, method, *args, **kwargs): if self._active_tool == self.__class__.tool_name: return method(*args, **kwargs) @@ -71,16 +71,16 @@ def _conanfile_run(self, command, accepted_returns): return ret def install_substitutes(self, *args, **kwargs): - return self._run_method(self._install_substitutes, *args, **kwargs) + return self.run(self._install_substitutes, *args, **kwargs) def install(self, *args, **kwargs): - return self._run_method(self._install, *args, **kwargs) + return self.run(self._install, *args, **kwargs) def update(self, *args, **kwargs): - return self._run_method(self._update, *args, **kwargs) + return self.run(self._update, *args, **kwargs) def check(self, *args, **kwargs): - return self._run_method(self._check, *args, **kwargs) + return self.run(self._check, *args, **kwargs) def _install_substitutes(self, *packages_substitutes, update=False, check=True, **kwargs): errors = [] @@ -118,8 +118,7 @@ def _install(self, packages, update=False, check=True, **kwargs): tool=self.tool_name, packages=" ".join(packages_arch), **kwargs) - accepted_returns = self.accepted_install_codes - return self._conanfile_run(command, accepted_returns) + return self._conanfile_run(command, self.accepted_install_codes) else: self._conanfile.output.info("System requirements: {} already " "installed".format(" ".join(packages))) @@ -134,8 +133,7 @@ def _update(self): "'-c tools.system.package_manager:mode={1}'".format(self.mode_check, self.mode_install)) command = self.update_command.format(sudo=self.sudo_str, tool=self.tool_name) - accepted_returns = self.accepted_update_codes - return self._conanfile_run(command, accepted_returns) + return self._conanfile_run(command, self.accepted_update_codes) def _check(self, packages): missing = [pkg for pkg in packages if self.check_package(self.get_package_name(pkg)) != 0] @@ -144,8 +142,7 @@ def _check(self, packages): def check_package(self, package): command = self.check_command.format(tool=self.tool_name, package=package) - accepted_returns = self.accepted_check_codes - return self._conanfile_run(command, accepted_returns) + return self._conanfile_run(command, self.accepted_check_codes) class Apt(_SystemPackageManagerTool):