link: use proper exceptions (#6852)

* link: use proper exceptions

* system: use ENOTSUP instead of NotImplementedErrorwq
This commit is contained in:
Ruslan Kuprieiev
2021-10-23 02:38:17 +03:00
committed by GitHub
parent 719e93192f
commit e2d705fd76
5 changed files with 42 additions and 43 deletions
+5 -2
View File
@@ -1,3 +1,4 @@
import errno
import itertools
import os
import pathlib
@@ -7,7 +8,7 @@ import uuid
import psutil
from dvc import __version__
from dvc.exceptions import DvcException, NotDvcRepoError
from dvc.exceptions import NotDvcRepoError
from dvc.fs import FS_MAP, get_fs_cls, get_fs_config
from dvc.repo import Repo
from dvc.scm.base import SCMError
@@ -103,7 +104,9 @@ def _get_linktype_support_info(repo):
if is_link and not is_link(dst):
status = "broken"
os.unlink(dst)
except DvcException:
except OSError as exc:
if exc.errno != errno.ENOTSUP:
raise
status = "not supported"
if status == "supported":
+4 -1
View File
@@ -1,3 +1,4 @@
import errno
import logging
from itertools import chain
@@ -77,7 +78,9 @@ def _try_links(cache, from_info, to_info, link_types):
_verify_link(cache, to_info, link_types[0])
return
except DvcException as exc:
except OSError as exc:
if exc.errno not in [errno.EXDEV, errno.ENOTSUP]:
raise
logger.debug(
"Cache type '%s' is not supported: %s", link_types[0], exc
)
+31 -38
View File
@@ -3,34 +3,29 @@ import logging
import os
import platform
from dvc.exceptions import DvcException
logger = logging.getLogger(__name__)
class System:
@staticmethod
def hardlink(source, link_name):
try:
os.link(source, link_name)
except OSError as exc:
raise DvcException("failed to link") from exc
os.link(source, link_name)
@staticmethod
def symlink(source, link_name):
try:
os.symlink(source, link_name)
except OSError as exc:
raise DvcException("failed to symlink") from exc
os.symlink(source, link_name)
@staticmethod
def _reflink_darwin(src, dst):
import ctypes
def _cdll(name):
return ctypes.CDLL(name, use_errno=True)
LIBC = "libc.dylib"
LIBC_FALLBACK = "/usr/lib/libSystem.dylib"
try:
clib = ctypes.CDLL(LIBC)
clib = _cdll(LIBC)
except OSError as exc:
logger.debug(
"unable to access '{}' (errno '{}'). "
@@ -39,40 +34,43 @@ class System:
if exc.errno != errno.ENOENT:
raise
# NOTE: trying to bypass System Integrity Protection (SIP)
clib = ctypes.CDLL(LIBC_FALLBACK)
clib = _cdll(LIBC_FALLBACK)
if not hasattr(clib, "clonefile"):
return -1
raise OSError(
errno.ENOTSUP,
"'clonefile' not supported by the standard library",
)
clonefile = clib.clonefile
clonefile.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
clonefile.restype = ctypes.c_int
return clonefile(
ret = clonefile(
ctypes.c_char_p(os.fsencode(src)),
ctypes.c_char_p(os.fsencode(dst)),
ctypes.c_int(0),
)
@staticmethod
def _reflink_windows(_src, _dst):
return -1
if ret:
err = ctypes.get_errno()
msg = os.strerror(err)
raise OSError(err, msg)
@staticmethod
def _reflink_linux(src, dst):
import fcntl # pylint: disable=import-error
from funcy import suppress
FICLONE = 0x40049409
try:
ret = 255
with open(src, "rb") as s, open(dst, "wb+") as d:
ret = fcntl.ioctl(d.fileno(), FICLONE, s.fileno())
finally:
if ret != 0:
fcntl.ioctl(d.fileno(), FICLONE, s.fileno())
except OSError:
with suppress(OSError):
os.unlink(dst)
return ret
raise
@staticmethod
def reflink(source, link_name):
@@ -81,20 +79,15 @@ class System:
source, link_name = os.fspath(source), os.fspath(link_name)
system = platform.system()
try:
if system == "Windows":
ret = System._reflink_windows(source, link_name)
elif system == "Darwin":
ret = System._reflink_darwin(source, link_name)
elif system == "Linux":
ret = System._reflink_linux(source, link_name)
else:
ret = -1
except OSError:
ret = -1
if ret != 0:
raise DvcException("reflink is not supported")
if system == "Darwin":
System._reflink_darwin(source, link_name)
elif system == "Linux":
System._reflink_linux(source, link_name)
else:
raise OSError(
errno.ENOTSUP,
f"reflink is not supported on '{system}'",
)
# NOTE: reflink has a new inode, but has the same mode as the src,
# so we need to chmod it to look like a normal copy.
+1 -1
View File
@@ -206,7 +206,7 @@ def copyfile(src, dest, callback=None, no_progress_bar=False, name=None):
try:
System.reflink(src, dest)
except DvcException:
except OSError:
from dvc.progress import tdqm_or_callback_wrapped
with open(src, "rb") as fsrc, open(dest, "wb+") as fdest:
+1 -1
View File
@@ -958,7 +958,7 @@ def test_add_with_cache_link_error(tmp_dir, dvc, mocker, capsys):
mocker.patch(
"dvc.objects.checkout._do_link",
side_effect=DvcException("link failed"),
side_effect=OSError(errno.ENOTSUP, "link failed"),
)
dvc.add("foo")
err = capsys.readouterr()[1]