Fixed #6547: Added the ignore_dangling_symlinks option to shutil.copytree
This commit is contained in:
parent
0d2fa4a612
commit
fb437513d6
|
@ -99,6 +99,12 @@ Directory and files operations
|
||||||
symbolic links in the new tree; if false or omitted, the contents of the
|
symbolic links in the new tree; if false or omitted, the contents of the
|
||||||
linked files are copied to the new tree.
|
linked files are copied to the new tree.
|
||||||
|
|
||||||
|
When *symlinks* is false, if the file pointed by the symlink doesn't
|
||||||
|
exist, a exception will be added in the list of errors raised in
|
||||||
|
a :exc:`Error` exception at the end of the copy process.
|
||||||
|
You can set the optional *ignore_dangling_symlinks* flag to true if you
|
||||||
|
want to silent this exception.
|
||||||
|
|
||||||
If *ignore* is given, it must be a callable that will receive as its
|
If *ignore* is given, it must be a callable that will receive as its
|
||||||
arguments the directory being visited by :func:`copytree`, and a list of its
|
arguments the directory being visited by :func:`copytree`, and a list of its
|
||||||
contents, as returned by :func:`os.listdir`. Since :func:`copytree` is
|
contents, as returned by :func:`os.listdir`. Since :func:`copytree` is
|
||||||
|
@ -120,6 +126,11 @@ Directory and files operations
|
||||||
Added the *copy_function* argument to be able to provide a custom copy
|
Added the *copy_function* argument to be able to provide a custom copy
|
||||||
function.
|
function.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.2
|
||||||
|
Added the *ignore_dangling_symlinks* argument to silent dangling symlinks
|
||||||
|
errors when *symlinks* is false.
|
||||||
|
|
||||||
|
|
||||||
.. function:: rmtree(path, ignore_errors=False, onerror=None)
|
.. function:: rmtree(path, ignore_errors=False, onerror=None)
|
||||||
|
|
||||||
.. index:: single: directory; deleting
|
.. index:: single: directory; deleting
|
||||||
|
|
|
@ -147,7 +147,8 @@ def ignore_patterns(*patterns):
|
||||||
return set(ignored_names)
|
return set(ignored_names)
|
||||||
return _ignore_patterns
|
return _ignore_patterns
|
||||||
|
|
||||||
def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2):
|
def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
|
||||||
|
ignore_dangling_symlinks=False):
|
||||||
"""Recursively copy a directory tree.
|
"""Recursively copy a directory tree.
|
||||||
|
|
||||||
The destination directory must not already exist.
|
The destination directory must not already exist.
|
||||||
|
@ -156,7 +157,13 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2):
|
||||||
If the optional symlinks flag is true, symbolic links in the
|
If the optional symlinks flag is true, symbolic links in the
|
||||||
source tree result in symbolic links in the destination tree; if
|
source tree result in symbolic links in the destination tree; if
|
||||||
it is false, the contents of the files pointed to by symbolic
|
it is false, the contents of the files pointed to by symbolic
|
||||||
links are copied.
|
links are copied. If the file pointed by the symlink doesn't
|
||||||
|
exist, an exception will be added in the list of errors raised in
|
||||||
|
an Error exception at the end of the copy process.
|
||||||
|
|
||||||
|
You can set the optional ignore_dangling_symlinks flag to true if you
|
||||||
|
want to silent this exception.
|
||||||
|
|
||||||
|
|
||||||
The optional ignore argument is a callable. If given, it
|
The optional ignore argument is a callable. If given, it
|
||||||
is called with the `src` parameter, which is the directory
|
is called with the `src` parameter, which is the directory
|
||||||
|
@ -190,9 +197,16 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2):
|
||||||
srcname = os.path.join(src, name)
|
srcname = os.path.join(src, name)
|
||||||
dstname = os.path.join(dst, name)
|
dstname = os.path.join(dst, name)
|
||||||
try:
|
try:
|
||||||
if symlinks and os.path.islink(srcname):
|
if os.path.islink(srcname):
|
||||||
linkto = os.readlink(srcname)
|
linkto = os.readlink(srcname)
|
||||||
|
if symlinks:
|
||||||
os.symlink(linkto, dstname)
|
os.symlink(linkto, dstname)
|
||||||
|
else:
|
||||||
|
# ignore dangling symlink if the flag is on
|
||||||
|
if not os.path.exists(linkto) and ignore_dangling_symlinks:
|
||||||
|
continue
|
||||||
|
# otherwise let the copy occurs. copy2 will raise an error
|
||||||
|
copy_function(srcname, dstname)
|
||||||
elif os.path.isdir(srcname):
|
elif os.path.isdir(srcname):
|
||||||
copytree(srcname, dstname, symlinks, ignore, copy_function)
|
copytree(srcname, dstname, symlinks, ignore, copy_function)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -13,7 +13,7 @@ from os.path import splitdrive
|
||||||
from distutils.spawn import find_executable, spawn
|
from distutils.spawn import find_executable, spawn
|
||||||
from shutil import (_make_tarball, _make_zipfile, make_archive,
|
from shutil import (_make_tarball, _make_zipfile, make_archive,
|
||||||
register_archive_format, unregister_archive_format,
|
register_archive_format, unregister_archive_format,
|
||||||
get_archive_formats)
|
get_archive_formats, Error)
|
||||||
import tarfile
|
import tarfile
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
@ -351,6 +351,26 @@ class TestShutil(unittest.TestCase):
|
||||||
shutil.copytree(src_dir, dst_dir, copy_function=_copy)
|
shutil.copytree(src_dir, dst_dir, copy_function=_copy)
|
||||||
self.assertEquals(len(copied), 2)
|
self.assertEquals(len(copied), 2)
|
||||||
|
|
||||||
|
def test_copytree_dangling_symlinks(self):
|
||||||
|
|
||||||
|
# a dangling symlink raises an error at the end
|
||||||
|
src_dir = self.mkdtemp()
|
||||||
|
dst_dir = os.path.join(self.mkdtemp(), 'destination')
|
||||||
|
os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt'))
|
||||||
|
os.mkdir(os.path.join(src_dir, 'test_dir'))
|
||||||
|
self._write_data(os.path.join(src_dir, 'test_dir', 'test.txt'), '456')
|
||||||
|
self.assertRaises(Error, shutil.copytree, src_dir, dst_dir)
|
||||||
|
|
||||||
|
# a dangling symlink is ignored with the proper flag
|
||||||
|
dst_dir = os.path.join(self.mkdtemp(), 'destination2')
|
||||||
|
shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)
|
||||||
|
self.assertNotIn('test.txt', os.listdir(dst_dir))
|
||||||
|
|
||||||
|
# a dangling symlink is copied if symlinks=True
|
||||||
|
dst_dir = os.path.join(self.mkdtemp(), 'destination3')
|
||||||
|
shutil.copytree(src_dir, dst_dir, symlinks=True)
|
||||||
|
self.assertIn('test.txt', os.listdir(dst_dir))
|
||||||
|
|
||||||
@unittest.skipUnless(zlib, "requires zlib")
|
@unittest.skipUnless(zlib, "requires zlib")
|
||||||
def test_make_tarball(self):
|
def test_make_tarball(self):
|
||||||
# creating something to tar
|
# creating something to tar
|
||||||
|
|
|
@ -318,6 +318,8 @@ C-API
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #6547: Added the ignore_dangling_symlinks option to shutil.copytree.
|
||||||
|
|
||||||
- Issue #1540112: Now allowing the choice of a copy function in
|
- Issue #1540112: Now allowing the choice of a copy function in
|
||||||
shutil.copytree.
|
shutil.copytree.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue