@@ -450,7 +450,7 @@ def _ignore_patterns(path, names):
450450 return _ignore_patterns
451451
452452def _copytree (entries , src , dst , symlinks , ignore , copy_function ,
453- ignore_dangling_symlinks , dirs_exist_ok = False ):
453+ copy_stat , ignore_dangling_symlinks , dirs_exist_ok = False ):
454454 if ignore is not None :
455455 ignored_names = ignore (os .fspath (src ), [x .name for x in entries ])
456456 else :
@@ -481,20 +481,22 @@ def _copytree(entries, src, dst, symlinks, ignore, copy_function,
481481 # code with a custom `copy_function` may rely on copytree
482482 # doing the right thing.
483483 os .symlink (linkto , dstname )
484- copystat (srcobj , dstname , follow_symlinks = not symlinks )
484+ if copy_stat :
485+ copystat (srcobj , dstname , follow_symlinks = not symlinks )
485486 else :
486487 # ignore dangling symlink if the flag is on
487488 if not os .path .exists (linkto ) and ignore_dangling_symlinks :
488489 continue
489490 # otherwise let the copy occur. copy2 will raise an error
490491 if srcentry .is_dir ():
491492 copytree (srcobj , dstname , symlinks , ignore ,
492- copy_function , dirs_exist_ok = dirs_exist_ok )
493+ copy_function , copy_stat ,
494+ dirs_exist_ok = dirs_exist_ok )
493495 else :
494496 copy_function (srcobj , dstname )
495497 elif srcentry .is_dir ():
496498 copytree (srcobj , dstname , symlinks , ignore , copy_function ,
497- dirs_exist_ok = dirs_exist_ok )
499+ copy_stat , dirs_exist_ok = dirs_exist_ok )
498500 else :
499501 # Will raise a SpecialFileError for unsupported file types
500502 copy_function (srcobj , dstname )
@@ -504,18 +506,20 @@ def _copytree(entries, src, dst, symlinks, ignore, copy_function,
504506 errors .extend (err .args [0 ])
505507 except OSError as why :
506508 errors .append ((srcname , dstname , str (why )))
507- try :
508- copystat (src , dst )
509- except OSError as why :
510- # Copying file access times may fail on Windows
511- if getattr (why , 'winerror' , None ) is None :
512- errors .append ((src , dst , str (why )))
509+ if copy_stat :
510+ try :
511+ copystat (src , dst )
512+ except OSError as why :
513+ # Copying file access times may fail on Windows
514+ if getattr (why , 'winerror' , None ) is None :
515+ errors .append ((src , dst , str (why )))
513516 if errors :
514517 raise Error (errors )
515518 return dst
516519
517520def copytree (src , dst , symlinks = False , ignore = None , copy_function = copy2 ,
518- ignore_dangling_symlinks = False , dirs_exist_ok = False ):
521+ copy_stat = True , ignore_dangling_symlinks = False ,
522+ dirs_exist_ok = False ):
519523 """Recursively copy a directory tree and return the destination directory.
520524
521525 dirs_exist_ok dictates whether to raise an exception in case dst or any
@@ -551,12 +555,17 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
551555 destination path as arguments. By default, copy2() is used, but any
552556 function that supports the same signature (like copy()) can be used.
553557
558+ The optional copy_stat argument controls whether permissions will be
559+ copied from the source to the destination. This can be used along with
560+ copy_function=copyfile when you wish to not use the source permissions
561+ at all.
554562 """
555563 sys .audit ("shutil.copytree" , src , dst )
556564 with os .scandir (src ) as itr :
557565 entries = list (itr )
558566 return _copytree (entries = entries , src = src , dst = dst , symlinks = symlinks ,
559567 ignore = ignore , copy_function = copy_function ,
568+ copy_stat = copy_stat ,
560569 ignore_dangling_symlinks = ignore_dangling_symlinks ,
561570 dirs_exist_ok = dirs_exist_ok )
562571
0 commit comments