mirror of https://github.com/python/cpython
Issue #8876: distutils now falls back to copying files when hard linking doesn't work.
This allows use with special filesystems such as VirtualBox shared folders.
This commit is contained in:
commit
aea7f4a8e2
|
@ -80,7 +80,8 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
|
|||
(os.symlink) instead of copying: set it to "hard" or "sym"; if it is
|
||||
None (the default), files are copied. Don't set 'link' on systems that
|
||||
don't support it: 'copy_file()' doesn't check if hard or symbolic
|
||||
linking is available.
|
||||
linking is available. If hardlink fails, falls back to
|
||||
_copy_file_contents().
|
||||
|
||||
Under Mac OS, uses the native file copy function in macostools; on
|
||||
other systems, uses '_copy_file_contents()' to copy file contents.
|
||||
|
@ -132,24 +133,31 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
|
|||
# (Unix only, of course, but that's the caller's responsibility)
|
||||
elif link == 'hard':
|
||||
if not (os.path.exists(dst) and os.path.samefile(src, dst)):
|
||||
os.link(src, dst)
|
||||
try:
|
||||
os.link(src, dst)
|
||||
return (dst, 1)
|
||||
except OSError:
|
||||
# If hard linking fails, fall back on copying file
|
||||
# (some special filesystems don't support hard linking
|
||||
# even under Unix, see issue #8876).
|
||||
pass
|
||||
elif link == 'sym':
|
||||
if not (os.path.exists(dst) and os.path.samefile(src, dst)):
|
||||
os.symlink(src, dst)
|
||||
return (dst, 1)
|
||||
|
||||
# Otherwise (non-Mac, not linking), copy the file contents and
|
||||
# (optionally) copy the times and mode.
|
||||
else:
|
||||
_copy_file_contents(src, dst)
|
||||
if preserve_mode or preserve_times:
|
||||
st = os.stat(src)
|
||||
_copy_file_contents(src, dst)
|
||||
if preserve_mode or preserve_times:
|
||||
st = os.stat(src)
|
||||
|
||||
# According to David Ascher <da@ski.org>, utime() should be done
|
||||
# before chmod() (at least under NT).
|
||||
if preserve_times:
|
||||
os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
|
||||
if preserve_mode:
|
||||
os.chmod(dst, S_IMODE(st[ST_MODE]))
|
||||
# According to David Ascher <da@ski.org>, utime() should be done
|
||||
# before chmod() (at least under NT).
|
||||
if preserve_times:
|
||||
os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
|
||||
if preserve_mode:
|
||||
os.chmod(dst, S_IMODE(st[ST_MODE]))
|
||||
|
||||
return (dst, 1)
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import shutil
|
|||
import errno
|
||||
from unittest.mock import patch
|
||||
|
||||
from distutils.file_util import move_file
|
||||
from distutils.file_util import move_file, copy_file
|
||||
from distutils import log
|
||||
from distutils.tests import support
|
||||
from distutils.errors import DistutilsFileError
|
||||
|
@ -78,6 +78,36 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
|
|||
fobj.write('spam eggs')
|
||||
move_file(self.source, self.target, verbose=0)
|
||||
|
||||
def test_copy_file_hard_link(self):
|
||||
with open(self.source, 'w') as f:
|
||||
f.write('some content')
|
||||
st = os.stat(self.source)
|
||||
copy_file(self.source, self.target, link='hard')
|
||||
st2 = os.stat(self.source)
|
||||
st3 = os.stat(self.target)
|
||||
self.assertTrue(os.path.samestat(st, st2), (st, st2))
|
||||
self.assertTrue(os.path.samestat(st2, st3), (st2, st3))
|
||||
with open(self.source, 'r') as f:
|
||||
self.assertEqual(f.read(), 'some content')
|
||||
|
||||
def test_copy_file_hard_link_failure(self):
|
||||
# If hard linking fails, copy_file() falls back on copying file
|
||||
# (some special filesystems don't support hard linking even under
|
||||
# Unix, see issue #8876).
|
||||
with open(self.source, 'w') as f:
|
||||
f.write('some content')
|
||||
st = os.stat(self.source)
|
||||
with patch("os.link", side_effect=OSError(0, "linking unsupported")):
|
||||
copy_file(self.source, self.target, link='hard')
|
||||
st2 = os.stat(self.source)
|
||||
st3 = os.stat(self.target)
|
||||
self.assertTrue(os.path.samestat(st, st2), (st, st2))
|
||||
self.assertFalse(os.path.samestat(st2, st3), (st2, st3))
|
||||
for fn in (self.source, self.target):
|
||||
with open(fn, 'r') as f:
|
||||
self.assertEqual(f.read(), 'some content')
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(FileUtilTestCase)
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ Core and Builtins
|
|||
argument contains not permitted null character or byte.
|
||||
|
||||
- Issue #22258: Fix the internal function set_inheritable() on Illumos.
|
||||
|
||||
This platform exposes the function ``ioctl(FIOCLEX)``, but calling it fails
|
||||
with errno is ENOTTY: "Inappropriate ioctl for device". set_inheritable()
|
||||
now falls back to the slower ``fcntl()`` (``F_GETFD`` and then ``F_SETFD``).
|
||||
|
@ -181,6 +180,10 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #8876: distutils now falls back to copying files when hard linking
|
||||
doesn't work. This allows use with special filesystems such as VirtualBox
|
||||
shared folders.
|
||||
|
||||
- Issue #22217: Implemented reprs of classes in the zipfile module.
|
||||
|
||||
- Issue #18216: gettext now raises an error when a .mo file has an
|
||||
|
|
Loading…
Reference in New Issue