diff --git a/dvc/remote/base.py b/dvc/remote/base.py index d515defec..22bbc307e 100644 --- a/dvc/remote/base.py +++ b/dvc/remote/base.py @@ -35,10 +35,12 @@ class DataCloudError(DvcException): super(DataCloudError, self).__init__("Data sync error: {}".format(msg)) -class RemoteBaseCmdError(DvcException): - def __init__(self, cmd, ret, err): - m = "SSH command '{}' finished with non-zero return code '{}': {}" - super(RemoteBaseCmdError, self).__init__(m.format(cmd, ret, err)) +class RemoteCmdError(DvcException): + def __init__(self, remote, cmd, ret, err): + super(RemoteCmdError, self).__init__( + "{remote} command '{cmd}' finished with non-zero return code" + " {ret}': {err}".format(remote=remote, cmd=cmd, ret=ret, err=err) + ) class RemoteMissingDepsError(DvcException): diff --git a/dvc/remote/hdfs.py b/dvc/remote/hdfs.py index 38bda2e72..2aa0a25ad 100644 --- a/dvc/remote/hdfs.py +++ b/dvc/remote/hdfs.py @@ -8,14 +8,10 @@ from subprocess import Popen, PIPE import dvc.logger as logger from dvc.config import Config -from dvc.remote.base import RemoteBase, RemoteBaseCmdError +from dvc.remote.base import RemoteBase, RemoteCmdError from dvc.utils import fix_env -class RemoteHDFSCmdError(RemoteBaseCmdError): - pass - - class RemoteHDFS(RemoteBase): scheme = "hdfs" REGEX = r"^hdfs://((?P.*)@)?.*$" @@ -55,7 +51,7 @@ class RemoteHDFS(RemoteBase): ) out, err = p.communicate() if p.returncode != 0: - raise RemoteHDFSCmdError(cmd, p.returncode, err) + raise RemoteCmdError(self.scheme, cmd, p.returncode, err) return out.decode("utf-8") @staticmethod @@ -123,7 +119,7 @@ class RemoteHDFS(RemoteBase): try: self.hadoop_fs("test -e {}".format(path_info["path"])) return True - except RemoteHDFSCmdError: + except RemoteCmdError: return False def upload(self, from_infos, to_infos, names=None): @@ -177,7 +173,7 @@ class RemoteHDFS(RemoteBase): def list_cache_paths(self): try: self.hadoop_fs("test -e {}".format(self.prefix)) - except RemoteHDFSCmdError: + except RemoteCmdError: return [] stdout = self.hadoop_fs("ls -R {}".format(self.prefix)) diff --git a/dvc/remote/ssh.py b/dvc/remote/ssh.py index 2fff01ddc..859b2daac 100644 --- a/dvc/remote/ssh.py +++ b/dvc/remote/ssh.py @@ -16,7 +16,7 @@ from dvc.config import Config from dvc.progress import progress from dvc.utils.compat import urlparse from dvc.exceptions import DvcException -from dvc.remote.base import RemoteBase, RemoteBaseCmdError +from dvc.remote.base import RemoteBase, RemoteCmdError def sizeof_fmt(num, suffix="B"): @@ -43,10 +43,6 @@ def create_cb(name): return lambda cur, tot: percent_cb(name, cur, tot) -class RemoteSSHCmdError(RemoteBaseCmdError): - pass - - class RemoteSSH(RemoteBase): scheme = "ssh" @@ -134,7 +130,7 @@ class RemoteSSH(RemoteBase): try: self._exec(ssh, "test -e {}".format(path_info["path"])) exists = True - except RemoteSSHCmdError: + except RemoteCmdError: exists = False return exists @@ -184,7 +180,7 @@ class RemoteSSH(RemoteBase): ret = stdout.channel.recv_exit_status() if ret != 0: err = b"".join(stderr_chunks).decode("utf-8") - raise RemoteSSHCmdError(cmd, ret, err) + raise RemoteCmdError(self.scheme, cmd, ret, err) return b"".join(stdout_chunks).decode("utf-8") diff --git a/tests/unit/remote/base.py b/tests/unit/remote/base.py index ecf434818..1199df216 100644 --- a/tests/unit/remote/base.py +++ b/tests/unit/remote/base.py @@ -1,8 +1,8 @@ import mock from unittest import TestCase -from dvc.remote.base import RemoteBase, RemoteMissingDepsError -from dvc.remote import REMOTES, RemoteLOCAL +from dvc.remote import REMOTES, RemoteLOCAL, RemoteSSH, RemoteHDFS +from dvc.remote.base import RemoteBase, RemoteCmdError, RemoteMissingDepsError class TestMissingDeps(TestCase): @@ -13,3 +13,23 @@ class TestMissingDeps(TestCase): with mock.patch.object(remote_class, "REQUIRES", REQUIRES): with self.assertRaises(RemoteMissingDepsError): remote_class(None, {}) + + +class TestCmdError(TestCase): + def test(self): + for remote_class in [RemoteSSH, RemoteHDFS]: + project = None + config = {} + + remote_name = remote_class.scheme + cmd = "sed 'hello'" + ret = "1" + err = "sed: expression #1, char 2: extra characters after command" + + with mock.patch.object( + remote_class, + "remove", + side_effect=RemoteCmdError(remote_name, cmd, ret, err), + ): + with self.assertRaises(RemoteCmdError): + remote_class(project, config).remove("file")