megfile 3.0.5__py3-none-any.whl → 3.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
megfile/s3.py CHANGED
@@ -169,14 +169,16 @@ def s3_hasbucket(path: PathLike) -> bool:
169
169
  return S3Path(path).hasbucket()
170
170
 
171
171
 
172
- def s3_move(src_url: PathLike, dst_url: PathLike) -> None:
172
+ def s3_move(
173
+ src_url: PathLike, dst_url: PathLike, overwrite: bool = True) -> None:
173
174
  '''
174
175
  Move file/directory path from src_url to dst_url
175
176
 
176
177
  :param src_url: Given path
177
178
  :param dst_url: Given destination path
179
+ :param overwrite: whether or not overwrite file when exists
178
180
  '''
179
- return S3Path(src_url).move(dst_url)
181
+ return S3Path(src_url).move(dst_url, overwrite)
180
182
 
181
183
 
182
184
  def s3_remove(path: PathLike, missing_ok: bool = False) -> None:
@@ -305,8 +307,9 @@ def s3_getmd5(
305
307
  def s3_copy(
306
308
  src_url: PathLike,
307
309
  dst_url: PathLike,
310
+ callback: Optional[Callable[[int], None]] = None,
308
311
  followlinks: bool = False,
309
- callback: Optional[Callable[[int], None]] = None) -> None:
312
+ overwrite: bool = True) -> None:
310
313
  ''' File copy on S3
311
314
  Copy content of file on `src_path` to `dst_path`.
312
315
  It's caller's responsebility to ensure the s3_isfile(src_url) == True
@@ -314,24 +317,28 @@ def s3_copy(
314
317
  :param src_url: Given path
315
318
  :param dst_path: Target file path
316
319
  :param callback: Called periodically during copy, and the input parameter is the data size (in bytes) of copy since the last call
320
+ :param followlinks: False if regard symlink as file, else True
321
+ :param overwrite: whether or not overwrite file when exists, default is True
317
322
  '''
318
- return S3Path(src_url).copy(dst_url, followlinks, callback)
323
+ return S3Path(src_url).copy(dst_url, callback, followlinks, overwrite)
319
324
 
320
325
 
321
326
  def s3_sync(
322
327
  src_url: PathLike,
323
328
  dst_url: PathLike,
324
329
  followlinks: bool = False,
325
- force: bool = False) -> None:
330
+ force: bool = False,
331
+ overwrite: bool = True) -> None:
326
332
  '''
327
333
  Copy file/directory on src_url to dst_url
328
334
 
329
335
  :param src_url: Given path
330
336
  :param dst_url: Given destination path
331
337
  :param followlinks: False if regard symlink as file, else True
332
- :param force: Sync file forcely, do not ignore same files
338
+ :param force: Sync file forcely, do not ignore same files, priority is higher than 'overwrite', default is False
339
+ :param overwrite: whether or not overwrite file when exists, default is True
333
340
  '''
334
- return S3Path(src_url).sync(dst_url, followlinks, force)
341
+ return S3Path(src_url).sync(dst_url, followlinks, force, overwrite)
335
342
 
336
343
 
337
344
  def s3_symlink(src_path: PathLike, dst_path: PathLike) -> None:
megfile/s3_path.py CHANGED
@@ -930,16 +930,29 @@ def s3_download(
930
930
  src_url: PathLike,
931
931
  dst_url: PathLike,
932
932
  followlinks: bool = False,
933
- callback: Optional[Callable[[int], None]] = None) -> None:
933
+ callback: Optional[Callable[[int], None]] = None,
934
+ overwrite: bool = True) -> None:
934
935
  '''
935
936
  Downloads a file from s3 to local filesystem.
936
937
  :param src_url: source s3 path
937
938
  :param dst_url: target fs path
938
939
  :param callback: Called periodically during copy, and the input parameter is the data size (in bytes) of copy since the last call
940
+ :param followlinks: False if regard symlink as file, else True
941
+ :param overwrite: whether or not overwrite file when exists, default is True
939
942
  '''
940
943
  from megfile.fs import is_fs
941
944
  from megfile.fs_path import FSPath
942
945
 
946
+ dst_url = fspath(dst_url)
947
+ if not is_fs(dst_url):
948
+ raise OSError(f'dst_url is not fs path: {dst_url}')
949
+ if not dst_url or dst_url.endswith('/'):
950
+ raise S3IsADirectoryError('Is a directory: %r' % dst_url)
951
+
952
+ dst_path = FSPath(dst_url)
953
+ if not overwrite and dst_path.exists():
954
+ return
955
+
943
956
  if not isinstance(src_url, S3Path):
944
957
  src_url = S3Path(src_url)
945
958
  if followlinks:
@@ -960,13 +973,6 @@ def s3_download(
960
973
  raise S3IsADirectoryError(
961
974
  'Is a directory: %r' % src_url.path_with_protocol)
962
975
 
963
- dst_url = fspath(dst_url)
964
- if not is_fs(dst_url):
965
- raise OSError(f'dst_url is not fs path: {dst_url}')
966
- if not dst_url or dst_url.endswith('/'):
967
- raise S3IsADirectoryError('Is a directory: %r' % dst_url)
968
-
969
- dst_path = FSPath(dst_url)
970
976
  dst_directory = os.path.dirname(dst_path.path_without_protocol)
971
977
  if dst_directory != '':
972
978
  os.makedirs(dst_directory, exist_ok=True)
@@ -997,12 +1003,14 @@ def s3_upload(
997
1003
  src_url: PathLike,
998
1004
  dst_url: PathLike,
999
1005
  callback: Optional[Callable[[int], None]] = None,
1006
+ overwrite: bool = True,
1000
1007
  **kwargs) -> None:
1001
1008
  '''
1002
1009
  Uploads a file from local filesystem to s3.
1003
1010
  :param src_url: source fs path
1004
1011
  :param dst_url: target s3 path
1005
1012
  :param callback: Called periodically during copy, and the input parameter is the data size (in bytes) of copy since the last call
1013
+ :param overwrite: whether or not overwrite file when exists, default is True
1006
1014
  '''
1007
1015
  from megfile.fs import is_fs
1008
1016
  from megfile.fs_path import FSPath
@@ -1017,6 +1025,9 @@ def s3_upload(
1017
1025
  if not dst_key or dst_key.endswith('/'):
1018
1026
  raise S3IsADirectoryError('Is a directory: %r' % dst_url)
1019
1027
 
1028
+ if not overwrite and S3Path(dst_url).is_file():
1029
+ return
1030
+
1020
1031
  client = get_s3_client_with_cache(
1021
1032
  profile_name=S3Path(dst_url)._profile_name)
1022
1033
  upload_fileobj = patch_method(
@@ -1086,18 +1097,15 @@ def s3_readlink(path) -> str:
1086
1097
  return S3Path(path).readlink().path_with_protocol
1087
1098
 
1088
1099
 
1089
- def s3_rename(src_url: PathLike, dst_url: PathLike):
1100
+ def s3_rename(
1101
+ src_url: PathLike, dst_url: PathLike, overwrite: bool = True) -> None:
1090
1102
  '''
1091
1103
  Move s3 file path from src_url to dst_url
1092
1104
 
1093
1105
  :param dst_url: Given destination path
1106
+ :param overwrite: whether or not overwrite file when exists
1094
1107
  '''
1095
- src_path_ins = S3Path(src_url)
1096
- if src_path_ins.is_file():
1097
- src_path_ins.copy(dst_url)
1098
- else:
1099
- src_path_ins.sync(dst_url)
1100
- src_path_ins.remove()
1108
+ S3Path(src_url).rename(dst_url, overwrite)
1101
1109
 
1102
1110
 
1103
1111
  class S3Cacher(FileCacher):
@@ -1692,15 +1700,16 @@ class S3Path(URIPath):
1692
1700
  if self.exists():
1693
1701
  raise S3FileExistsError('File exists: %r' % self.path_with_protocol)
1694
1702
 
1695
- def move(self, dst_url: PathLike) -> None:
1703
+ def move(self, dst_url: PathLike, overwrite: bool = True) -> None:
1696
1704
  '''
1697
1705
  Move file/directory path from src_url to dst_url
1698
1706
 
1699
1707
  :param dst_url: Given destination path
1708
+ :param overwrite: whether or not overwrite file when exists
1700
1709
  '''
1701
1710
  for src_file_path, dst_file_path in _s3_scan_pairs(
1702
1711
  self.path_with_protocol, dst_url):
1703
- S3Path(src_file_path).rename(dst_file_path)
1712
+ S3Path(src_file_path).rename(dst_file_path, overwrite)
1704
1713
 
1705
1714
  def remove(self, missing_ok: bool = False) -> None:
1706
1715
  '''
@@ -1774,13 +1783,18 @@ class S3Path(URIPath):
1774
1783
  raise S3UnknownError(
1775
1784
  Exception(error_msg), self.path_with_protocol)
1776
1785
 
1777
- def rename(self, dst_path: PathLike) -> 'S3Path':
1786
+ def rename(self, dst_path: PathLike, overwrite: bool = True) -> 'S3Path':
1778
1787
  '''
1779
1788
  Move s3 file path from src_url to dst_url
1780
1789
 
1781
1790
  :param dst_path: Given destination path
1791
+ :param overwrite: whether or not overwrite file when exists
1782
1792
  '''
1783
- s3_rename(self.path_with_protocol, dst_path)
1793
+ if self.is_file():
1794
+ self.copy(dst_path, overwrite=overwrite)
1795
+ else:
1796
+ self.sync(dst_path, overwrite=overwrite)
1797
+ self.remove(missing_ok=True)
1784
1798
  return self.from_path(dst_path)
1785
1799
 
1786
1800
  def scan(self, missing_ok: bool = True,
@@ -2111,15 +2125,21 @@ class S3Path(URIPath):
2111
2125
  def copy(
2112
2126
  self,
2113
2127
  dst_url: PathLike,
2128
+ callback: Optional[Callable[[int], None]] = None,
2114
2129
  followlinks: bool = False,
2115
- callback: Optional[Callable[[int], None]] = None) -> None:
2130
+ overwrite: bool = True) -> None:
2116
2131
  ''' File copy on S3
2117
2132
  Copy content of file on `src_path` to `dst_path`.
2118
2133
  It's caller's responsebility to ensure the s3_isfile(src_url) == True
2119
2134
 
2120
2135
  :param dst_path: Target file path
2121
2136
  :param callback: Called periodically during copy, and the input parameter is the data size (in bytes) of copy since the last call
2137
+ :param followlinks: False if regard symlink as file, else True
2138
+ :param overwrite: whether or not overwrite file when exists, default is True
2122
2139
  '''
2140
+ if not overwrite and self.from_path(dst_url).is_file():
2141
+ return
2142
+
2123
2143
  src_url = self.path_with_protocol
2124
2144
  src_bucket, src_key = parse_s3_url(src_url)
2125
2145
  dst_bucket, dst_key = parse_s3_url(dst_url)
@@ -2156,22 +2176,32 @@ class S3Path(URIPath):
2156
2176
  Callback=callback)
2157
2177
 
2158
2178
  def sync(
2159
- self, dst_url: PathLike, followlinks: bool = False,
2160
- force: bool = False) -> None:
2179
+ self,
2180
+ dst_url: PathLike,
2181
+ followlinks: bool = False,
2182
+ force: bool = False,
2183
+ overwrite: bool = True) -> None:
2161
2184
  '''
2162
2185
  Copy file/directory on src_url to dst_url
2163
2186
 
2164
2187
  :param dst_url: Given destination path
2165
2188
  :param followlinks: False if regard symlink as file, else True
2166
- :param force: Sync file forcely, do not ignore same files
2189
+ :param force: Sync file forcely, do not ignore same files, priority is higher than 'overwrite', default is False
2190
+ :param overwrite: whether or not overwrite file when exists, default is True
2167
2191
  '''
2168
2192
  for src_file_path, dst_file_path in _s3_scan_pairs(
2169
2193
  self.path_with_protocol, dst_url):
2170
2194
  src_file_path = self.from_path(src_file_path)
2171
2195
  dst_file_path = self.from_path(dst_file_path)
2172
- if not force and dst_file_path.exists() and is_same_file(
2196
+
2197
+ if force:
2198
+ pass
2199
+ elif not overwrite and dst_file_path.exists():
2200
+ continue
2201
+ elif dst_file_path.exists() and is_same_file(
2173
2202
  src_file_path.stat(), dst_file_path.stat(), 'copy'):
2174
2203
  continue
2204
+
2175
2205
  src_file_path.copy(dst_file_path, followlinks=followlinks)
2176
2206
 
2177
2207
  def symlink(self, dst_path: PathLike) -> None:
@@ -2181,7 +2211,7 @@ class S3Path(URIPath):
2181
2211
  :param dst_path: Desination path
2182
2212
  :raises: S3NameTooLongError, S3BucketNotFoundError, S3IsADirectoryError
2183
2213
  '''
2184
- if len(str(self._s3_path).encode()) > 1024:
2214
+ if len(fspath(self._s3_path).encode()) > 1024:
2185
2215
  raise S3NameTooLongError('File name too long: %r' % dst_path)
2186
2216
  src_bucket, src_key = parse_s3_url(self.path_with_protocol)
2187
2217
  dst_bucket, dst_key = parse_s3_url(dst_path)
megfile/sftp.py CHANGED
@@ -162,24 +162,30 @@ def sftp_realpath(path: PathLike) -> str:
162
162
  return SftpPath(path).realpath()
163
163
 
164
164
 
165
- def sftp_rename(src_path: PathLike, dst_path: PathLike) -> 'SftpPath':
165
+ def sftp_rename(
166
+ src_path: PathLike, dst_path: PathLike,
167
+ overwrite: bool = True) -> 'SftpPath':
166
168
  '''
167
169
  rename file on sftp
168
170
 
169
171
  :param src_path: Given path
170
172
  :param dst_path: Given destination path
173
+ :param overwrite: whether or not overwrite file when exists
171
174
  '''
172
- return SftpPath(src_path).rename(dst_path)
175
+ return SftpPath(src_path).rename(dst_path, overwrite)
173
176
 
174
177
 
175
- def sftp_move(src_path: PathLike, dst_path: PathLike) -> 'SftpPath':
178
+ def sftp_move(
179
+ src_path: PathLike, dst_path: PathLike,
180
+ overwrite: bool = True) -> 'SftpPath':
176
181
  '''
177
182
  move file on sftp
178
183
 
179
184
  :param src_path: Given path
180
185
  :param dst_path: Given destination path
186
+ :param overwrite: whether or not overwrite file when exists
181
187
  '''
182
- return SftpPath(src_path).replace(dst_path)
188
+ return SftpPath(src_path).replace(dst_path, overwrite)
183
189
 
184
190
 
185
191
  def sftp_remove(path: PathLike, missing_ok: bool = False) -> None:
@@ -369,7 +375,8 @@ def sftp_copy(
369
375
  src_path: PathLike,
370
376
  dst_path: PathLike,
371
377
  callback: Optional[Callable[[int], None]] = None,
372
- followlinks: bool = False):
378
+ followlinks: bool = False,
379
+ overwrite: bool = True):
373
380
  """
374
381
  Copy the file to the given destination path.
375
382
 
@@ -381,19 +388,21 @@ def sftp_copy(
381
388
  :raises IsADirectoryError: If the source is a directory.
382
389
  :raises OSError: If there is an error copying the file.
383
390
  """
384
- return SftpPath(src_path).copy(dst_path, callback, followlinks)
391
+ return SftpPath(src_path).copy(dst_path, callback, followlinks, overwrite)
385
392
 
386
393
 
387
394
  def sftp_sync(
388
395
  src_path: PathLike,
389
396
  dst_path: PathLike,
390
397
  followlinks: bool = False,
391
- force: bool = False):
398
+ force: bool = False,
399
+ overwrite: bool = True):
392
400
  '''Copy file/directory on src_url to dst_url
393
401
 
394
402
  :param src_path: Given path
395
403
  :param dst_url: Given destination path
396
404
  :param followlinks: False if regard symlink as file, else True
397
- :param force: Sync file forcely, do not ignore same files
405
+ :param force: Sync file forcely, do not ignore same files, priority is higher than 'overwrite', default is False
406
+ :param overwrite: whether or not overwrite file when exists, default is True
398
407
  '''
399
- return SftpPath(src_path).sync(dst_path, followlinks, force)
408
+ return SftpPath(src_path).sync(dst_path, followlinks, force, overwrite)
megfile/sftp_path.py CHANGED
@@ -417,9 +417,15 @@ def sftp_download(
417
417
  src_url: PathLike,
418
418
  dst_url: PathLike,
419
419
  callback: Optional[Callable[[int], None]] = None,
420
- followlinks: bool = False):
420
+ followlinks: bool = False,
421
+ overwrite: bool = True):
421
422
  '''
422
- File download
423
+ Downloads a file from sftp to local filesystem.
424
+ :param src_url: source sftp path
425
+ :param dst_url: target fs path
426
+ :param callback: Called periodically during copy, and the input parameter is the data size (in bytes) of copy since the last call
427
+ :param followlinks: False if regard symlink as file, else True
428
+ :param overwrite: whether or not overwrite file when exists, default is True
423
429
  '''
424
430
  from megfile.fs import is_fs
425
431
  from megfile.fs_path import FSPath
@@ -429,6 +435,10 @@ def sftp_download(
429
435
  if not is_sftp(src_url) and not isinstance(src_url, SftpPath):
430
436
  raise OSError(f'src_url is not sftp path: {src_url}')
431
437
 
438
+ dst_path = FSPath(dst_url)
439
+ if not overwrite and dst_path.exists():
440
+ return
441
+
432
442
  if isinstance(src_url, SftpPath):
433
443
  src_path = src_url
434
444
  else:
@@ -441,7 +451,6 @@ def sftp_download(
441
451
  if str(dst_url).endswith('/'):
442
452
  raise IsADirectoryError('Is a directory: %r' % dst_url)
443
453
 
444
- dst_path = FSPath(dst_url)
445
454
  dst_path.parent.makedirs(exist_ok=True)
446
455
 
447
456
  sftp_callback = None
@@ -467,9 +476,14 @@ def sftp_upload(
467
476
  src_url: PathLike,
468
477
  dst_url: PathLike,
469
478
  callback: Optional[Callable[[int], None]] = None,
470
- followlinks: bool = False):
479
+ followlinks: bool = False,
480
+ overwrite: bool = True):
471
481
  '''
472
- File upload
482
+ Uploads a file from local filesystem to sftp server.
483
+ :param src_url: source fs path
484
+ :param dst_url: target sftp path
485
+ :param callback: Called periodically during copy, and the input parameter is the data size (in bytes) of copy since the last call
486
+ :param overwrite: whether or not overwrite file when exists, default is True
473
487
  '''
474
488
  from megfile.fs import is_fs
475
489
  from megfile.fs_path import FSPath
@@ -491,6 +505,9 @@ def sftp_upload(
491
505
  dst_path = dst_url
492
506
  else:
493
507
  dst_path = SftpPath(dst_url)
508
+ if not overwrite and dst_path.exists():
509
+ return
510
+
494
511
  dst_path.parent.makedirs(exist_ok=True)
495
512
 
496
513
  sftp_callback = None
@@ -859,53 +876,57 @@ class SftpPath(URIPath):
859
876
  def _is_same_protocol(self, path):
860
877
  return is_sftp(path)
861
878
 
862
- def rename(self, dst_path: PathLike) -> 'SftpPath':
879
+ def rename(self, dst_path: PathLike, overwrite: bool = True) -> 'SftpPath':
863
880
  '''
864
881
  rename file on sftp
865
882
 
866
883
  :param dst_path: Given destination path
884
+ :param overwrite: whether or not overwrite file when exists
867
885
  '''
868
886
  if not self._is_same_protocol(dst_path):
869
887
  raise OSError('Not a %s path: %r' % (self.protocol, dst_path))
870
- if str(dst_path).endswith('/'):
871
- raise IsADirectoryError('Is a directory: %r' % dst_path)
872
888
 
873
- dst_path = self.from_path(dst_path)
889
+ dst_path = self.from_path(str(dst_path).rstrip('/'))
890
+
874
891
  src_stat = self.stat()
875
892
 
876
893
  if self._is_same_backend(dst_path):
877
- try:
894
+ if overwrite:
895
+ dst_path.remove(missing_ok=True)
878
896
  self._client.rename(self._real_path, dst_path._real_path)
879
- except OSError:
880
- if dst_path.exists():
881
- raise FileExistsError('File exists: %s' % dst_path)
897
+ else:
898
+ self.sync(dst_path, overwrite=overwrite)
899
+ self.remove(missing_ok=True)
882
900
  else:
883
901
  if self.is_dir():
884
902
  for file_entry in self.scandir():
885
903
  self.from_path(file_entry.path).rename(
886
904
  dst_path.joinpath(file_entry.name))
905
+ self._client.rmdir(self._real_path)
887
906
  else:
888
- with self.open('rb') as fsrc:
889
- with dst_path.open('wb') as fdst:
890
- length = 16 * 1024
891
- while True:
892
- buf = fsrc.read(length)
893
- if not buf:
894
- break
895
- fdst.write(buf)
907
+ if overwrite or not dst_path.exists():
908
+ with self.open('rb') as fsrc:
909
+ with dst_path.open('wb') as fdst:
910
+ length = 16 * 1024
911
+ while True:
912
+ buf = fsrc.read(length)
913
+ if not buf:
914
+ break
915
+ fdst.write(buf)
896
916
  self.unlink()
897
917
 
898
918
  dst_path.utime(src_stat.st_atime, src_stat.st_mtime)
899
919
  dst_path.chmod(src_stat.st_mode)
900
920
  return dst_path
901
921
 
902
- def replace(self, dst_path: PathLike) -> 'SftpPath':
922
+ def replace(self, dst_path: PathLike, overwrite: bool = True) -> 'SftpPath':
903
923
  '''
904
924
  move file on sftp
905
925
 
906
926
  :param dst_path: Given destination path
927
+ :param overwrite: whether or not overwrite file when exists
907
928
  '''
908
- return self.rename(dst_path=dst_path)
929
+ return self.rename(dst_path=dst_path, overwrite=overwrite)
909
930
 
910
931
  def remove(self, missing_ok: bool = False) -> None:
911
932
  '''
@@ -1235,7 +1256,8 @@ class SftpPath(URIPath):
1235
1256
  self,
1236
1257
  dst_path: PathLike,
1237
1258
  callback: Optional[Callable[[int], None]] = None,
1238
- followlinks: bool = False):
1259
+ followlinks: bool = False,
1260
+ overwrite: bool = True):
1239
1261
  """
1240
1262
  Copy the file to the given destination path.
1241
1263
 
@@ -1258,6 +1280,9 @@ class SftpPath(URIPath):
1258
1280
  raise IsADirectoryError(
1259
1281
  'Is a directory: %r' % self.path_with_protocol)
1260
1282
 
1283
+ if not overwrite and self.from_path(dst_path).exists():
1284
+ return
1285
+
1261
1286
  self.from_path(os.path.dirname(dst_path)).makedirs(exist_ok=True)
1262
1287
  dst_path = self.from_path(dst_path)
1263
1288
  if self._is_same_backend(dst_path):
@@ -1292,12 +1317,14 @@ class SftpPath(URIPath):
1292
1317
  self,
1293
1318
  dst_path: PathLike,
1294
1319
  followlinks: bool = False,
1295
- force: bool = False):
1320
+ force: bool = False,
1321
+ overwrite: bool = True):
1296
1322
  '''Copy file/directory on src_url to dst_url
1297
1323
 
1298
1324
  :param dst_url: Given destination path
1299
1325
  :param followlinks: False if regard symlink as file, else True
1300
- :param force: Sync file forcely, do not ignore same files
1326
+ :param force: Sync file forcely, do not ignore same files, priority is higher than 'overwrite', default is False
1327
+ :param overwrite: whether or not overwrite file when exists, default is True
1301
1328
  '''
1302
1329
  if not self._is_same_protocol(dst_path):
1303
1330
  raise OSError('Not a %s path: %r' % (self.protocol, dst_path))
@@ -1306,11 +1333,15 @@ class SftpPath(URIPath):
1306
1333
  self.path_with_protocol, dst_path):
1307
1334
  dst_path = self.from_path(dst_file_path)
1308
1335
  src_path = self.from_path(src_file_path)
1309
- if not force and dst_path.exists() and is_same_file(
1310
- src_path.stat(), dst_path.stat(), 'copy'):
1336
+
1337
+ if force:
1338
+ pass
1339
+ elif not overwrite and dst_path.exists():
1311
1340
  continue
1312
- self.from_path(os.path.dirname(dst_file_path)).mkdir(
1313
- parents=True, exist_ok=True)
1341
+ elif dst_path.exists() and is_same_file(src_path.stat(),
1342
+ dst_path.stat(), 'copy'):
1343
+ continue
1344
+
1314
1345
  self.from_path(src_file_path).copy(
1315
1346
  dst_file_path, followlinks=followlinks)
1316
1347