mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-05 23:09:32 +01:00
link: use proper exceptions (#6852)
* link: use proper exceptions * system: use ENOTSUP instead of NotImplementedErrorwq
This commit is contained in:
+5
-2
@@ -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":
|
||||
|
||||
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user