29
29
from pathlib ._os import (
30
30
PathInfo , DirEntryInfo ,
31
31
ensure_different_files , ensure_distinct_paths ,
32
- copy_file , copy_info ,
32
+ copyfile2 , copyfileobj , magic_open , copy_info ,
33
33
)
34
34
35
35
@@ -810,12 +810,6 @@ def write_text(self, data, encoding=None, errors=None, newline=None):
810
810
with self .open (mode = 'w' , encoding = encoding , errors = errors , newline = newline ) as f :
811
811
return f .write (data )
812
812
813
- def _write_info (self , info , follow_symlinks = True ):
814
- """
815
- Write the given PathInfo to this path.
816
- """
817
- copy_info (info , self , follow_symlinks = follow_symlinks )
818
-
819
813
_remove_leading_dot = operator .itemgetter (slice (2 , None ))
820
814
_remove_trailing_slash = operator .itemgetter (slice (- 1 ))
821
815
@@ -1100,18 +1094,21 @@ def replace(self, target):
1100
1094
target = self .with_segments (target )
1101
1095
return target
1102
1096
1103
- def copy (self , target , follow_symlinks = True , preserve_metadata = False ):
1097
+ def copy (self , target , ** kwargs ):
1104
1098
"""
1105
1099
Recursively copy this file or directory tree to the given destination.
1106
1100
"""
1107
1101
if not hasattr (target , 'with_segments' ):
1108
1102
target = self .with_segments (target )
1109
1103
ensure_distinct_paths (self , target )
1110
- copy_file (self , target , follow_symlinks , preserve_metadata )
1104
+ try :
1105
+ copy_to_target = target ._copy_from
1106
+ except AttributeError :
1107
+ raise TypeError (f"Target path is not writable: { target !r} " ) from None
1108
+ copy_to_target (self , ** kwargs )
1111
1109
return target .joinpath () # Empty join to ensure fresh metadata.
1112
1110
1113
- def copy_into (self , target_dir , * , follow_symlinks = True ,
1114
- preserve_metadata = False ):
1111
+ def copy_into (self , target_dir , ** kwargs ):
1115
1112
"""
1116
1113
Copy this file or directory tree into the given existing directory.
1117
1114
"""
@@ -1122,8 +1119,59 @@ def copy_into(self, target_dir, *, follow_symlinks=True,
1122
1119
target = target_dir / name
1123
1120
else :
1124
1121
target = self .with_segments (target_dir , name )
1125
- return self .copy (target , follow_symlinks = follow_symlinks ,
1126
- preserve_metadata = preserve_metadata )
1122
+ return self .copy (target , ** kwargs )
1123
+
1124
+ def _copy_from (self , source , follow_symlinks = True , preserve_metadata = False ):
1125
+ """
1126
+ Recursively copy the given path to this path.
1127
+ """
1128
+ if not follow_symlinks and source .info .is_symlink ():
1129
+ self ._copy_from_symlink (source , preserve_metadata )
1130
+ elif source .info .is_dir ():
1131
+ children = source .iterdir ()
1132
+ os .mkdir (self )
1133
+ for child in children :
1134
+ self .joinpath (child .name )._copy_from (
1135
+ child , follow_symlinks , preserve_metadata )
1136
+ if preserve_metadata :
1137
+ copy_info (source .info , self )
1138
+ else :
1139
+ self ._copy_from_file (source , preserve_metadata )
1140
+
1141
+ def _copy_from_file (self , source , preserve_metadata = False ):
1142
+ ensure_different_files (source , self )
1143
+ with magic_open (source , 'rb' ) as source_f :
1144
+ with open (self , 'wb' ) as target_f :
1145
+ copyfileobj (source_f , target_f )
1146
+ if preserve_metadata :
1147
+ copy_info (source .info , self )
1148
+
1149
+ if copyfile2 :
1150
+ # Use fast OS routine for local file copying where available.
1151
+ _copy_from_file_fallback = _copy_from_file
1152
+ def _copy_from_file (self , source , preserve_metadata = False ):
1153
+ try :
1154
+ source = os .fspath (source )
1155
+ except TypeError :
1156
+ pass
1157
+ else :
1158
+ copyfile2 (source , str (self ))
1159
+ return
1160
+ self ._copy_from_file_fallback (source , preserve_metadata )
1161
+
1162
+ if os .name == 'nt' :
1163
+ # If a directory-symlink is copied *before* its target, then
1164
+ # os.symlink() incorrectly creates a file-symlink on Windows. Avoid
1165
+ # this by passing *target_is_dir* to os.symlink() on Windows.
1166
+ def _copy_from_symlink (self , source , preserve_metadata = False ):
1167
+ os .symlink (str (source .readlink ()), self , source .info .is_dir ())
1168
+ if preserve_metadata :
1169
+ copy_info (source .info , self , follow_symlinks = False )
1170
+ else :
1171
+ def _copy_from_symlink (self , source , preserve_metadata = False ):
1172
+ os .symlink (str (source .readlink ()), self )
1173
+ if preserve_metadata :
1174
+ copy_info (source .info , self , follow_symlinks = False )
1127
1175
1128
1176
def move (self , target ):
1129
1177
"""
0 commit comments