-
-
Notifications
You must be signed in to change notification settings - Fork 32.6k
bpo-45558: shutil.copytree: Allow disabling copystat #29130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -450,7 +450,7 @@ def _ignore_patterns(path, names): | |
return _ignore_patterns | ||
|
||
def _copytree(entries, src, dst, symlinks, ignore, copy_function, | ||
ignore_dangling_symlinks, dirs_exist_ok=False): | ||
copy_stat, ignore_dangling_symlinks, dirs_exist_ok=False): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Do you mean to add this to the docs of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, sorry, I commented on the wrong location. This should be added to the docs of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK I think understood you correctly. |
||
if ignore is not None: | ||
ignored_names = ignore(os.fspath(src), [x.name for x in entries]) | ||
else: | ||
|
@@ -481,20 +481,22 @@ def _copytree(entries, src, dst, symlinks, ignore, copy_function, | |
# code with a custom `copy_function` may rely on copytree | ||
# doing the right thing. | ||
os.symlink(linkto, dstname) | ||
copystat(srcobj, dstname, follow_symlinks=not symlinks) | ||
if copy_stat: | ||
copystat(srcobj, dstname, follow_symlinks=not symlinks) | ||
else: | ||
# ignore dangling symlink if the flag is on | ||
if not os.path.exists(linkto) and ignore_dangling_symlinks: | ||
continue | ||
# otherwise let the copy occur. copy2 will raise an error | ||
if srcentry.is_dir(): | ||
copytree(srcobj, dstname, symlinks, ignore, | ||
copy_function, dirs_exist_ok=dirs_exist_ok) | ||
copy_function, copy_stat, | ||
dirs_exist_ok=dirs_exist_ok) | ||
else: | ||
copy_function(srcobj, dstname) | ||
elif srcentry.is_dir(): | ||
copytree(srcobj, dstname, symlinks, ignore, copy_function, | ||
dirs_exist_ok=dirs_exist_ok) | ||
copy_stat, dirs_exist_ok=dirs_exist_ok) | ||
else: | ||
# Will raise a SpecialFileError for unsupported file types | ||
copy_function(srcobj, dstname) | ||
|
@@ -504,18 +506,20 @@ def _copytree(entries, src, dst, symlinks, ignore, copy_function, | |
errors.extend(err.args[0]) | ||
except OSError as why: | ||
errors.append((srcname, dstname, str(why))) | ||
try: | ||
copystat(src, dst) | ||
except OSError as why: | ||
# Copying file access times may fail on Windows | ||
if getattr(why, 'winerror', None) is None: | ||
errors.append((src, dst, str(why))) | ||
if copy_stat: | ||
try: | ||
copystat(src, dst) | ||
except OSError as why: | ||
# Copying file access times may fail on Windows | ||
if getattr(why, 'winerror', None) is None: | ||
errors.append((src, dst, str(why))) | ||
if errors: | ||
raise Error(errors) | ||
return dst | ||
|
||
def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, | ||
ignore_dangling_symlinks=False, dirs_exist_ok=False): | ||
copy_stat=True, ignore_dangling_symlinks=False, | ||
dirs_exist_ok=False): | ||
"""Recursively copy a directory tree and return the destination directory. | ||
|
||
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, | |
destination path as arguments. By default, copy2() is used, but any | ||
function that supports the same signature (like copy()) can be used. | ||
|
||
The optional copy_stat argument controls whether permissions will be | ||
copied from the source to the destination. This can be used along with | ||
copy_function=copyfile when you wish to not use the source permissions | ||
at all. | ||
doronbehar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
sys.audit("shutil.copytree", src, dst) | ||
with os.scandir(src) as itr: | ||
entries = list(itr) | ||
return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks, | ||
ignore=ignore, copy_function=copy_function, | ||
copy_stat=copy_stat, | ||
ignore_dangling_symlinks=ignore_dangling_symlinks, | ||
dirs_exist_ok=dirs_exist_ok) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
:func:`shutil.copytree`: Add a ``copy_stat`` boolean option that controls whether permissions will be copied as well from the source directory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inserting this new parameter before existing ones will break existing uses if passed positionally. Was this position chosen for a reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The position was chosen so it'd be near the
copy_function
parameter.