Browse Source

Intermediate changes

robot-piglet 7 months ago
parent
commit
f3da1e8b8d
1 changed files with 37 additions and 30 deletions
  1. 37 30
      library/python/fs/__init__.py

+ 37 - 30
library/python/fs/__init__.py

@@ -196,32 +196,6 @@ def hardlink(src, lnk):
         os.link(src, lnk)
 
 
-@errorfix_win
-def hardlink_or_copy(src, lnk):
-    def should_fallback_to_copy(exc):
-        if WindowsError is not None and isinstance(exc, WindowsError) and exc.winerror == 1142:  # too many hardlinks
-            return True
-        # cross-device hardlink or too many hardlinks, or some known WSL error
-        if isinstance(exc, OSError) and exc.errno in (
-            errno.EXDEV,
-            errno.EMLINK,
-            errno.EINVAL,
-            errno.EACCES,
-            errno.EPERM,
-        ):
-            return True
-        return False
-
-    try:
-        hardlink(src, lnk)
-    except Exception as e:
-        logger.debug('Failed to hardlink %s to %s with error %s, will copy it', src, lnk, repr(e))
-        if should_fallback_to_copy(e):
-            copy2(src, lnk, follow_symlinks=False)
-        else:
-            raise
-
-
 # Atomic file/directory symlink (Unix only)
 # Dst must not exist
 # Throws OSError
@@ -247,24 +221,57 @@ def copy2(src, lnk, follow_symlinks=True):
     symlink(os.readlink(src), lnk)
 
 
+def copy2_safe(src, lnk, follow_symlinks=True):
+    try:
+        copy2(src, lnk, follow_symlinks)
+    except shutil.SameFileError:
+        pass
+
+
+@errorfix_win
+def hardlink_or_copy(src, lnk, copy_function=copy2):
+    def should_fallback_to_copy(exc):
+        if WindowsError is not None and isinstance(exc, WindowsError) and exc.winerror == 1142:  # too many hardlinks
+            return True
+        # cross-device hardlink or too many hardlinks, or some known WSL error
+        if isinstance(exc, OSError) and exc.errno in (
+            errno.EXDEV,
+            errno.EMLINK,
+            errno.EINVAL,
+            errno.EACCES,
+            errno.EPERM,
+        ):
+            return True
+        return False
+
+    try:
+        hardlink(src, lnk)
+    except Exception as e:
+        logger.debug('Failed to hardlink %s to %s with error %s, will copy it', src, lnk, repr(e))
+        if should_fallback_to_copy(e):
+            copy_function(src, lnk, follow_symlinks=False)
+        else:
+            raise
+
+
 # Recursively hardlink directory
 # Uses plain hardlink for files
 # Dst must not exist
 # Non-atomic
 # Throws OSError
 @errorfix_win
-def hardlink_tree(src, dst):
+def hardlink_tree(src, dst, hardlink_function=hardlink, mkdir_function=os.mkdir):
     if not os.path.exists(src):
         raise CustomFsError(errno.ENOENT, filename=src)
     if os.path.isfile(src):
-        hardlink(src, dst)
+        hardlink_function(src, dst)
         return
     for dirpath, _, filenames in walk_relative(src):
         src_dirpath = os.path.join(src, dirpath) if dirpath != '.' else src
         dst_dirpath = os.path.join(dst, dirpath) if dirpath != '.' else dst
-        os.mkdir(dst_dirpath)
+        mkdir_function(dst_dirpath)
         for filename in filenames:
-            hardlink(os.path.join(src_dirpath, filename), os.path.join(dst_dirpath, filename))
+            hardlink_function(os.path.join(src_dirpath, filename), os.path.join(dst_dirpath, filename))
 
 
 # File copy