blob: 636e3bd9795593f2e4b62835ef4996ab5939392e [file] [log] [blame]
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001# Copyright (C) 2003 Python Software Foundation
2
3import unittest
Berker Peksag884afd92014-12-10 02:50:32 +02004import unittest.mock
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00005import shutil
6import tempfile
Johannes Gijsbers8e6f2de2004-11-23 09:27:27 +00007import sys
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +00008import stat
Brett Cannon1c3fa182004-06-19 21:11:35 +00009import os
10import os.path
Antoine Pitrouc041ab62012-01-02 19:18:02 +010011import errno
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040012import functools
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -070013import pathlib
Antoine Pitroubcf2b592012-02-08 23:28:36 +010014import subprocess
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020015import random
16import string
17import contextlib
18import io
Serhiy Storchaka527ef072015-09-06 18:33:19 +030019from shutil import (make_archive,
Tarek Ziadé396fad72010-02-23 05:30:31 +000020 register_archive_format, unregister_archive_format,
Tarek Ziadé6ac91722010-04-28 17:51:36 +000021 get_archive_formats, Error, unpack_archive,
22 register_unpack_format, RegistryError,
Hynek Schlawack48653762012-10-07 12:49:58 +020023 unregister_unpack_format, get_unpack_formats,
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020024 SameFileError, _GiveupOnFastCopy)
Tarek Ziadé396fad72010-02-23 05:30:31 +000025import tarfile
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +020026import zipfile
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020027try:
28 import posix
29except ImportError:
30 posix = None
Tarek Ziadé396fad72010-02-23 05:30:31 +000031
32from test import support
Serhiy Storchakab21d1552018-03-02 11:53:51 +020033from test.support import TESTFN, FakePath
Serhiy Storchaka11213772014-08-06 18:50:19 +030034
Antoine Pitrou7fff0962009-05-01 21:09:44 +000035TESTFN2 = TESTFN + "2"
Victor Stinner937ee9e2018-06-26 02:11:06 +020036MACOS = sys.platform.startswith("darwin")
Michael Feltef110b12019-02-18 12:02:44 +010037AIX = sys.platform[:3] == 'aix'
Tarek Ziadé396fad72010-02-23 05:30:31 +000038try:
39 import grp
40 import pwd
41 UID_GID_SUPPORT = True
42except ImportError:
43 UID_GID_SUPPORT = False
44
Steve Dower9eb3d542019-08-21 15:52:42 -070045try:
46 import _winapi
47except ImportError:
48 _winapi = None
49
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040050def _fake_rename(*args, **kwargs):
51 # Pretend the destination path is on a different filesystem.
Antoine Pitrouc041ab62012-01-02 19:18:02 +010052 raise OSError(getattr(errno, 'EXDEV', 18), "Invalid cross-device link")
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040053
54def mock_rename(func):
55 @functools.wraps(func)
56 def wrap(*args, **kwargs):
57 try:
58 builtin_rename = os.rename
59 os.rename = _fake_rename
60 return func(*args, **kwargs)
61 finally:
62 os.rename = builtin_rename
63 return wrap
64
Éric Araujoa7e33a12011-08-12 19:51:35 +020065def write_file(path, content, binary=False):
66 """Write *content* to a file located at *path*.
67
68 If *path* is a tuple instead of a string, os.path.join will be used to
69 make a path. If *binary* is true, the file will be opened in binary
70 mode.
71 """
72 if isinstance(path, tuple):
73 path = os.path.join(*path)
74 with open(path, 'wb' if binary else 'w') as fp:
75 fp.write(content)
76
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020077def write_test_file(path, size):
78 """Create a test file with an arbitrary size and random text content."""
79 def chunks(total, step):
80 assert total >= step
81 while total > step:
82 yield step
83 total -= step
84 if total:
85 yield total
86
87 bufsize = min(size, 8192)
88 chunk = b"".join([random.choice(string.ascii_letters).encode()
89 for i in range(bufsize)])
90 with open(path, 'wb') as f:
91 for csize in chunks(size, bufsize):
92 f.write(chunk)
93 assert os.path.getsize(path) == size
94
Éric Araujoa7e33a12011-08-12 19:51:35 +020095def read_file(path, binary=False):
96 """Return contents from a file located at *path*.
97
98 If *path* is a tuple instead of a string, os.path.join will be used to
99 make a path. If *binary* is true, the file will be opened in binary
100 mode.
101 """
102 if isinstance(path, tuple):
103 path = os.path.join(*path)
104 with open(path, 'rb' if binary else 'r') as fp:
105 return fp.read()
106
Serhiy Storchaka527ef072015-09-06 18:33:19 +0300107def rlistdir(path):
108 res = []
109 for name in sorted(os.listdir(path)):
110 p = os.path.join(path, name)
111 if os.path.isdir(p) and not os.path.islink(p):
112 res.append(name + '/')
113 for n in rlistdir(p):
114 res.append(name + '/' + n)
115 else:
116 res.append(name)
117 return res
118
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +0200119def supports_file2file_sendfile():
120 # ...apparently Linux and Solaris are the only ones
121 if not hasattr(os, "sendfile"):
122 return False
123 srcname = None
124 dstname = None
125 try:
126 with tempfile.NamedTemporaryFile("wb", delete=False) as f:
127 srcname = f.name
128 f.write(b"0123456789")
129
130 with open(srcname, "rb") as src:
131 with tempfile.NamedTemporaryFile("wb", delete=False) as dst:
Miss Islington (bot)7fe81ce2019-06-26 16:57:49 -0700132 dstname = dst.name
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +0200133 infd = src.fileno()
134 outfd = dst.fileno()
135 try:
136 os.sendfile(outfd, infd, 0, 2)
137 except OSError:
138 return False
139 else:
140 return True
141 finally:
142 if srcname is not None:
143 support.unlink(srcname)
144 if dstname is not None:
145 support.unlink(dstname)
146
147
148SUPPORTS_SENDFILE = supports_file2file_sendfile()
149
Michael Feltef110b12019-02-18 12:02:44 +0100150# AIX 32-bit mode, by default, lacks enough memory for the xz/lzma compiler test
151# The AIX command 'dump -o program' gives XCOFF header information
152# The second word of the last line in the maxdata value
153# when 32-bit maxdata must be greater than 0x1000000 for the xz test to succeed
154def _maxdataOK():
155 if AIX and sys.maxsize == 2147483647:
156 hdrs=subprocess.getoutput("/usr/bin/dump -o %s" % sys.executable)
157 maxdata=hdrs.split("\n")[-1].split()[1]
158 return int(maxdata,16) >= 0x20000000
159 else:
160 return True
Éric Araujoa7e33a12011-08-12 19:51:35 +0200161
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000162class TestShutil(unittest.TestCase):
Tarek Ziadé396fad72010-02-23 05:30:31 +0000163
164 def setUp(self):
165 super(TestShutil, self).setUp()
166 self.tempdirs = []
167
168 def tearDown(self):
169 super(TestShutil, self).tearDown()
170 while self.tempdirs:
171 d = self.tempdirs.pop()
172 shutil.rmtree(d, os.name in ('nt', 'cygwin'))
173
Tarek Ziadé396fad72010-02-23 05:30:31 +0000174
175 def mkdtemp(self):
176 """Create a temporary directory that will be cleaned up.
177
178 Returns the path of the directory.
179 """
180 d = tempfile.mkdtemp()
181 self.tempdirs.append(d)
182 return d
Tarek Ziadé5340db32010-04-19 22:30:51 +0000183
Hynek Schlawack3b527782012-06-25 13:27:31 +0200184 def test_rmtree_works_on_bytes(self):
185 tmp = self.mkdtemp()
186 victim = os.path.join(tmp, 'killme')
187 os.mkdir(victim)
188 write_file(os.path.join(victim, 'somefile'), 'foo')
189 victim = os.fsencode(victim)
190 self.assertIsInstance(victim, bytes)
Steve Dowere58571b2016-09-08 11:11:13 -0700191 shutil.rmtree(victim)
Hynek Schlawack3b527782012-06-25 13:27:31 +0200192
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200193 @support.skip_unless_symlink
194 def test_rmtree_fails_on_symlink(self):
195 tmp = self.mkdtemp()
196 dir_ = os.path.join(tmp, 'dir')
197 os.mkdir(dir_)
198 link = os.path.join(tmp, 'link')
199 os.symlink(dir_, link)
200 self.assertRaises(OSError, shutil.rmtree, link)
201 self.assertTrue(os.path.exists(dir_))
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100202 self.assertTrue(os.path.lexists(link))
203 errors = []
204 def onerror(*args):
205 errors.append(args)
206 shutil.rmtree(link, onerror=onerror)
207 self.assertEqual(len(errors), 1)
208 self.assertIs(errors[0][0], os.path.islink)
209 self.assertEqual(errors[0][1], link)
210 self.assertIsInstance(errors[0][2][1], OSError)
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200211
212 @support.skip_unless_symlink
213 def test_rmtree_works_on_symlinks(self):
214 tmp = self.mkdtemp()
215 dir1 = os.path.join(tmp, 'dir1')
216 dir2 = os.path.join(dir1, 'dir2')
217 dir3 = os.path.join(tmp, 'dir3')
218 for d in dir1, dir2, dir3:
219 os.mkdir(d)
220 file1 = os.path.join(tmp, 'file1')
221 write_file(file1, 'foo')
222 link1 = os.path.join(dir1, 'link1')
223 os.symlink(dir2, link1)
224 link2 = os.path.join(dir1, 'link2')
225 os.symlink(dir3, link2)
226 link3 = os.path.join(dir1, 'link3')
227 os.symlink(file1, link3)
228 # make sure symlinks are removed but not followed
229 shutil.rmtree(dir1)
230 self.assertFalse(os.path.exists(dir1))
231 self.assertTrue(os.path.exists(dir3))
232 self.assertTrue(os.path.exists(file1))
233
Steve Dower9eb3d542019-08-21 15:52:42 -0700234 @unittest.skipUnless(_winapi, 'only relevant on Windows')
235 def test_rmtree_fails_on_junctions(self):
236 tmp = self.mkdtemp()
237 dir_ = os.path.join(tmp, 'dir')
238 os.mkdir(dir_)
239 link = os.path.join(tmp, 'link')
240 _winapi.CreateJunction(dir_, link)
241 self.assertRaises(OSError, shutil.rmtree, link)
242 self.assertTrue(os.path.exists(dir_))
243 self.assertTrue(os.path.lexists(link))
244 errors = []
245 def onerror(*args):
246 errors.append(args)
247 shutil.rmtree(link, onerror=onerror)
248 self.assertEqual(len(errors), 1)
249 self.assertIs(errors[0][0], os.path.islink)
250 self.assertEqual(errors[0][1], link)
251 self.assertIsInstance(errors[0][2][1], OSError)
252
253 @unittest.skipUnless(_winapi, 'only relevant on Windows')
254 def test_rmtree_works_on_junctions(self):
255 tmp = self.mkdtemp()
256 dir1 = os.path.join(tmp, 'dir1')
257 dir2 = os.path.join(dir1, 'dir2')
258 dir3 = os.path.join(tmp, 'dir3')
259 for d in dir1, dir2, dir3:
260 os.mkdir(d)
261 file1 = os.path.join(tmp, 'file1')
262 write_file(file1, 'foo')
263 link1 = os.path.join(dir1, 'link1')
264 _winapi.CreateJunction(dir2, link1)
265 link2 = os.path.join(dir1, 'link2')
266 _winapi.CreateJunction(dir3, link2)
267 link3 = os.path.join(dir1, 'link3')
268 _winapi.CreateJunction(file1, link3)
269 # make sure junctions are removed but not followed
270 shutil.rmtree(dir1)
271 self.assertFalse(os.path.exists(dir1))
272 self.assertTrue(os.path.exists(dir3))
273 self.assertTrue(os.path.exists(file1))
274
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000275 def test_rmtree_errors(self):
276 # filename is guaranteed not to exist
277 filename = tempfile.mktemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100278 self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
279 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100280 shutil.rmtree(filename, ignore_errors=True)
281
282 # existing file
283 tmpdir = self.mkdtemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100284 write_file((tmpdir, "tstfile"), "")
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100285 filename = os.path.join(tmpdir, "tstfile")
Hynek Schlawackb5501102012-12-10 09:11:25 +0100286 with self.assertRaises(NotADirectoryError) as cm:
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100287 shutil.rmtree(filename)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100288 # The reason for this rather odd construct is that Windows sprinkles
289 # a \*.* at the end of file names. But only sometimes on some buildbots
290 possible_args = [filename, os.path.join(filename, '*.*')]
291 self.assertIn(cm.exception.filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100292 self.assertTrue(os.path.exists(filename))
Hynek Schlawackb5501102012-12-10 09:11:25 +0100293 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100294 shutil.rmtree(filename, ignore_errors=True)
295 self.assertTrue(os.path.exists(filename))
296 errors = []
297 def onerror(*args):
298 errors.append(args)
299 shutil.rmtree(filename, onerror=onerror)
300 self.assertEqual(len(errors), 2)
Serhiy Storchakad4d79bc2017-11-04 14:16:35 +0200301 self.assertIs(errors[0][0], os.scandir)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100302 self.assertEqual(errors[0][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100303 self.assertIsInstance(errors[0][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100304 self.assertIn(errors[0][2][1].filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100305 self.assertIs(errors[1][0], os.rmdir)
306 self.assertEqual(errors[1][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100307 self.assertIsInstance(errors[1][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100308 self.assertIn(errors[1][2][1].filename, possible_args)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000309
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000310
Serhiy Storchaka43767632013-11-03 21:31:38 +0200311 @unittest.skipIf(sys.platform[:6] == 'cygwin',
312 "This test can't be run on Cygwin (issue #1071513).")
313 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
314 "This test can't be run reliably as root (issue #1076467).")
315 def test_on_error(self):
316 self.errorState = 0
317 os.mkdir(TESTFN)
318 self.addCleanup(shutil.rmtree, TESTFN)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200319
Serhiy Storchaka43767632013-11-03 21:31:38 +0200320 self.child_file_path = os.path.join(TESTFN, 'a')
321 self.child_dir_path = os.path.join(TESTFN, 'b')
322 support.create_empty_file(self.child_file_path)
323 os.mkdir(self.child_dir_path)
324 old_dir_mode = os.stat(TESTFN).st_mode
325 old_child_file_mode = os.stat(self.child_file_path).st_mode
326 old_child_dir_mode = os.stat(self.child_dir_path).st_mode
327 # Make unwritable.
328 new_mode = stat.S_IREAD|stat.S_IEXEC
329 os.chmod(self.child_file_path, new_mode)
330 os.chmod(self.child_dir_path, new_mode)
331 os.chmod(TESTFN, new_mode)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000332
Serhiy Storchaka43767632013-11-03 21:31:38 +0200333 self.addCleanup(os.chmod, TESTFN, old_dir_mode)
334 self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
335 self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200336
Serhiy Storchaka43767632013-11-03 21:31:38 +0200337 shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
338 # Test whether onerror has actually been called.
339 self.assertEqual(self.errorState, 3,
340 "Expected call to onerror function did not happen.")
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000341
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000342 def check_args_to_onerror(self, func, arg, exc):
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000343 # test_rmtree_errors deliberately runs rmtree
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200344 # on a directory that is chmod 500, which will fail.
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000345 # This function is run when shutil.rmtree fails.
346 # 99.9% of the time it initially fails to remove
347 # a file in the directory, so the first time through
348 # func is os.remove.
349 # However, some Linux machines running ZFS on
350 # FUSE experienced a failure earlier in the process
351 # at os.listdir. The first failure may legally
352 # be either.
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200353 if self.errorState < 2:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200354 if func is os.unlink:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200355 self.assertEqual(arg, self.child_file_path)
356 elif func is os.rmdir:
357 self.assertEqual(arg, self.child_dir_path)
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000358 else:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200359 self.assertIs(func, os.listdir)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200360 self.assertIn(arg, [TESTFN, self.child_dir_path])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000361 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200362 self.errorState += 1
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000363 else:
364 self.assertEqual(func, os.rmdir)
365 self.assertEqual(arg, TESTFN)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000366 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200367 self.errorState = 3
368
369 def test_rmtree_does_not_choke_on_failing_lstat(self):
370 try:
371 orig_lstat = os.lstat
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200372 def raiser(fn, *args, **kwargs):
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200373 if fn != TESTFN:
374 raise OSError()
375 else:
376 return orig_lstat(fn)
377 os.lstat = raiser
378
379 os.mkdir(TESTFN)
380 write_file((TESTFN, 'foo'), 'foo')
381 shutil.rmtree(TESTFN)
382 finally:
383 os.lstat = orig_lstat
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000384
Antoine Pitrou78091e62011-12-29 18:54:15 +0100385 @support.skip_unless_symlink
386 def test_copymode_follow_symlinks(self):
387 tmp_dir = self.mkdtemp()
388 src = os.path.join(tmp_dir, 'foo')
389 dst = os.path.join(tmp_dir, 'bar')
390 src_link = os.path.join(tmp_dir, 'baz')
391 dst_link = os.path.join(tmp_dir, 'quux')
392 write_file(src, 'foo')
393 write_file(dst, 'foo')
394 os.symlink(src, src_link)
395 os.symlink(dst, dst_link)
396 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
397 # file to file
398 os.chmod(dst, stat.S_IRWXO)
399 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
400 shutil.copymode(src, dst)
401 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
Antoine Pitrou3f48ac92014-01-01 02:50:45 +0100402 # On Windows, os.chmod does not follow symlinks (issue #15411)
403 if os.name != 'nt':
404 # follow src link
405 os.chmod(dst, stat.S_IRWXO)
406 shutil.copymode(src_link, dst)
407 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
408 # follow dst link
409 os.chmod(dst, stat.S_IRWXO)
410 shutil.copymode(src, dst_link)
411 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
412 # follow both links
413 os.chmod(dst, stat.S_IRWXO)
414 shutil.copymode(src_link, dst_link)
415 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100416
417 @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
418 @support.skip_unless_symlink
419 def test_copymode_symlink_to_symlink(self):
420 tmp_dir = self.mkdtemp()
421 src = os.path.join(tmp_dir, 'foo')
422 dst = os.path.join(tmp_dir, 'bar')
423 src_link = os.path.join(tmp_dir, 'baz')
424 dst_link = os.path.join(tmp_dir, 'quux')
425 write_file(src, 'foo')
426 write_file(dst, 'foo')
427 os.symlink(src, src_link)
428 os.symlink(dst, dst_link)
429 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
430 os.chmod(dst, stat.S_IRWXU)
431 os.lchmod(src_link, stat.S_IRWXO|stat.S_IRWXG)
432 # link to link
433 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700434 shutil.copymode(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100435 self.assertEqual(os.lstat(src_link).st_mode,
436 os.lstat(dst_link).st_mode)
437 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
438 # src link - use chmod
439 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700440 shutil.copymode(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100441 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
442 # dst link - use chmod
443 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700444 shutil.copymode(src, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100445 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
446
447 @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing')
448 @support.skip_unless_symlink
449 def test_copymode_symlink_to_symlink_wo_lchmod(self):
450 tmp_dir = self.mkdtemp()
451 src = os.path.join(tmp_dir, 'foo')
452 dst = os.path.join(tmp_dir, 'bar')
453 src_link = os.path.join(tmp_dir, 'baz')
454 dst_link = os.path.join(tmp_dir, 'quux')
455 write_file(src, 'foo')
456 write_file(dst, 'foo')
457 os.symlink(src, src_link)
458 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700459 shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail
Antoine Pitrou78091e62011-12-29 18:54:15 +0100460
461 @support.skip_unless_symlink
462 def test_copystat_symlinks(self):
463 tmp_dir = self.mkdtemp()
464 src = os.path.join(tmp_dir, 'foo')
465 dst = os.path.join(tmp_dir, 'bar')
466 src_link = os.path.join(tmp_dir, 'baz')
467 dst_link = os.path.join(tmp_dir, 'qux')
468 write_file(src, 'foo')
469 src_stat = os.stat(src)
470 os.utime(src, (src_stat.st_atime,
471 src_stat.st_mtime - 42.0)) # ensure different mtimes
472 write_file(dst, 'bar')
473 self.assertNotEqual(os.stat(src).st_mtime, os.stat(dst).st_mtime)
474 os.symlink(src, src_link)
475 os.symlink(dst, dst_link)
476 if hasattr(os, 'lchmod'):
477 os.lchmod(src_link, stat.S_IRWXO)
478 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
479 os.lchflags(src_link, stat.UF_NODUMP)
480 src_link_stat = os.lstat(src_link)
481 # follow
482 if hasattr(os, 'lchmod'):
Larry Hastingsb4038062012-07-15 10:57:38 -0700483 shutil.copystat(src_link, dst_link, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100484 self.assertNotEqual(src_link_stat.st_mode, os.stat(dst).st_mode)
485 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700486 shutil.copystat(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100487 dst_link_stat = os.lstat(dst_link)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700488 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100489 for attr in 'st_atime', 'st_mtime':
490 # The modification times may be truncated in the new file.
491 self.assertLessEqual(getattr(src_link_stat, attr),
492 getattr(dst_link_stat, attr) + 1)
493 if hasattr(os, 'lchmod'):
494 self.assertEqual(src_link_stat.st_mode, dst_link_stat.st_mode)
495 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
496 self.assertEqual(src_link_stat.st_flags, dst_link_stat.st_flags)
497 # tell to follow but dst is not a link
Larry Hastingsb4038062012-07-15 10:57:38 -0700498 shutil.copystat(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100499 self.assertTrue(abs(os.stat(src).st_mtime - os.stat(dst).st_mtime) <
500 00000.1)
501
Ned Deilybaf75712012-05-10 17:05:19 -0700502 @unittest.skipUnless(hasattr(os, 'chflags') and
503 hasattr(errno, 'EOPNOTSUPP') and
504 hasattr(errno, 'ENOTSUP'),
505 "requires os.chflags, EOPNOTSUPP & ENOTSUP")
506 def test_copystat_handles_harmless_chflags_errors(self):
507 tmpdir = self.mkdtemp()
508 file1 = os.path.join(tmpdir, 'file1')
509 file2 = os.path.join(tmpdir, 'file2')
510 write_file(file1, 'xxx')
511 write_file(file2, 'xxx')
512
513 def make_chflags_raiser(err):
514 ex = OSError()
515
Larry Hastings90867a52012-06-22 17:01:41 -0700516 def _chflags_raiser(path, flags, *, follow_symlinks=True):
Ned Deilybaf75712012-05-10 17:05:19 -0700517 ex.errno = err
518 raise ex
519 return _chflags_raiser
520 old_chflags = os.chflags
521 try:
522 for err in errno.EOPNOTSUPP, errno.ENOTSUP:
523 os.chflags = make_chflags_raiser(err)
524 shutil.copystat(file1, file2)
525 # assert others errors break it
526 os.chflags = make_chflags_raiser(errno.EOPNOTSUPP + errno.ENOTSUP)
527 self.assertRaises(OSError, shutil.copystat, file1, file2)
528 finally:
529 os.chflags = old_chflags
530
Antoine Pitrou424246f2012-05-12 19:02:01 +0200531 @support.skip_unless_xattr
532 def test_copyxattr(self):
533 tmp_dir = self.mkdtemp()
534 src = os.path.join(tmp_dir, 'foo')
535 write_file(src, 'foo')
536 dst = os.path.join(tmp_dir, 'bar')
537 write_file(dst, 'bar')
538
539 # no xattr == no problem
540 shutil._copyxattr(src, dst)
541 # common case
542 os.setxattr(src, 'user.foo', b'42')
543 os.setxattr(src, 'user.bar', b'43')
544 shutil._copyxattr(src, dst)
Gregory P. Smith1093bf22014-01-17 12:01:22 -0800545 self.assertEqual(sorted(os.listxattr(src)), sorted(os.listxattr(dst)))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200546 self.assertEqual(
547 os.getxattr(src, 'user.foo'),
548 os.getxattr(dst, 'user.foo'))
549 # check errors don't affect other attrs
550 os.remove(dst)
551 write_file(dst, 'bar')
552 os_error = OSError(errno.EPERM, 'EPERM')
553
Larry Hastings9cf065c2012-06-22 16:30:09 -0700554 def _raise_on_user_foo(fname, attr, val, **kwargs):
Antoine Pitrou424246f2012-05-12 19:02:01 +0200555 if attr == 'user.foo':
556 raise os_error
557 else:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700558 orig_setxattr(fname, attr, val, **kwargs)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200559 try:
560 orig_setxattr = os.setxattr
561 os.setxattr = _raise_on_user_foo
562 shutil._copyxattr(src, dst)
Antoine Pitrou61597d32012-05-12 23:37:35 +0200563 self.assertIn('user.bar', os.listxattr(dst))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200564 finally:
565 os.setxattr = orig_setxattr
Hynek Schlawack0beab052013-02-05 08:22:44 +0100566 # the source filesystem not supporting xattrs should be ok, too.
567 def _raise_on_src(fname, *, follow_symlinks=True):
568 if fname == src:
569 raise OSError(errno.ENOTSUP, 'Operation not supported')
570 return orig_listxattr(fname, follow_symlinks=follow_symlinks)
571 try:
572 orig_listxattr = os.listxattr
573 os.listxattr = _raise_on_src
574 shutil._copyxattr(src, dst)
575 finally:
576 os.listxattr = orig_listxattr
Antoine Pitrou424246f2012-05-12 19:02:01 +0200577
Larry Hastingsad5ae042012-07-14 17:55:11 -0700578 # test that shutil.copystat copies xattrs
579 src = os.path.join(tmp_dir, 'the_original')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500580 srcro = os.path.join(tmp_dir, 'the_original_ro')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700581 write_file(src, src)
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500582 write_file(srcro, srcro)
Larry Hastingsad5ae042012-07-14 17:55:11 -0700583 os.setxattr(src, 'user.the_value', b'fiddly')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500584 os.setxattr(srcro, 'user.the_value', b'fiddly')
585 os.chmod(srcro, 0o444)
Larry Hastingsad5ae042012-07-14 17:55:11 -0700586 dst = os.path.join(tmp_dir, 'the_copy')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500587 dstro = os.path.join(tmp_dir, 'the_copy_ro')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700588 write_file(dst, dst)
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500589 write_file(dstro, dstro)
Larry Hastingsad5ae042012-07-14 17:55:11 -0700590 shutil.copystat(src, dst)
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500591 shutil.copystat(srcro, dstro)
Hynek Schlawackc2d481f2012-07-16 17:11:10 +0200592 self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500593 self.assertEqual(os.getxattr(dstro, 'user.the_value'), b'fiddly')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700594
Antoine Pitrou424246f2012-05-12 19:02:01 +0200595 @support.skip_unless_symlink
596 @support.skip_unless_xattr
597 @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,
598 'root privileges required')
599 def test_copyxattr_symlinks(self):
600 # On Linux, it's only possible to access non-user xattr for symlinks;
601 # which in turn require root privileges. This test should be expanded
602 # as soon as other platforms gain support for extended attributes.
603 tmp_dir = self.mkdtemp()
604 src = os.path.join(tmp_dir, 'foo')
605 src_link = os.path.join(tmp_dir, 'baz')
606 write_file(src, 'foo')
607 os.symlink(src, src_link)
608 os.setxattr(src, 'trusted.foo', b'42')
Larry Hastings9cf065c2012-06-22 16:30:09 -0700609 os.setxattr(src_link, 'trusted.foo', b'43', follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200610 dst = os.path.join(tmp_dir, 'bar')
611 dst_link = os.path.join(tmp_dir, 'qux')
612 write_file(dst, 'bar')
613 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700614 shutil._copyxattr(src_link, dst_link, follow_symlinks=False)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700615 self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43')
Antoine Pitrou424246f2012-05-12 19:02:01 +0200616 self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo')
Larry Hastingsb4038062012-07-15 10:57:38 -0700617 shutil._copyxattr(src_link, dst, follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200618 self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43')
619
Antoine Pitrou78091e62011-12-29 18:54:15 +0100620 @support.skip_unless_symlink
621 def test_copy_symlinks(self):
622 tmp_dir = self.mkdtemp()
623 src = os.path.join(tmp_dir, 'foo')
624 dst = os.path.join(tmp_dir, 'bar')
625 src_link = os.path.join(tmp_dir, 'baz')
626 write_file(src, 'foo')
627 os.symlink(src, src_link)
628 if hasattr(os, 'lchmod'):
629 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
630 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700631 shutil.copy(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100632 self.assertFalse(os.path.islink(dst))
633 self.assertEqual(read_file(src), read_file(dst))
634 os.remove(dst)
635 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700636 shutil.copy(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100637 self.assertTrue(os.path.islink(dst))
638 self.assertEqual(os.readlink(dst), os.readlink(src_link))
639 if hasattr(os, 'lchmod'):
640 self.assertEqual(os.lstat(src_link).st_mode,
641 os.lstat(dst).st_mode)
642
643 @support.skip_unless_symlink
644 def test_copy2_symlinks(self):
645 tmp_dir = self.mkdtemp()
646 src = os.path.join(tmp_dir, 'foo')
647 dst = os.path.join(tmp_dir, 'bar')
648 src_link = os.path.join(tmp_dir, 'baz')
649 write_file(src, 'foo')
650 os.symlink(src, src_link)
651 if hasattr(os, 'lchmod'):
652 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
653 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
654 os.lchflags(src_link, stat.UF_NODUMP)
655 src_stat = os.stat(src)
656 src_link_stat = os.lstat(src_link)
657 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700658 shutil.copy2(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100659 self.assertFalse(os.path.islink(dst))
660 self.assertEqual(read_file(src), read_file(dst))
661 os.remove(dst)
662 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700663 shutil.copy2(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100664 self.assertTrue(os.path.islink(dst))
665 self.assertEqual(os.readlink(dst), os.readlink(src_link))
666 dst_stat = os.lstat(dst)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700667 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100668 for attr in 'st_atime', 'st_mtime':
669 # The modification times may be truncated in the new file.
670 self.assertLessEqual(getattr(src_link_stat, attr),
671 getattr(dst_stat, attr) + 1)
672 if hasattr(os, 'lchmod'):
673 self.assertEqual(src_link_stat.st_mode, dst_stat.st_mode)
674 self.assertNotEqual(src_stat.st_mode, dst_stat.st_mode)
675 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
676 self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags)
677
Antoine Pitrou424246f2012-05-12 19:02:01 +0200678 @support.skip_unless_xattr
679 def test_copy2_xattr(self):
680 tmp_dir = self.mkdtemp()
681 src = os.path.join(tmp_dir, 'foo')
682 dst = os.path.join(tmp_dir, 'bar')
683 write_file(src, 'foo')
684 os.setxattr(src, 'user.foo', b'42')
685 shutil.copy2(src, dst)
686 self.assertEqual(
687 os.getxattr(src, 'user.foo'),
688 os.getxattr(dst, 'user.foo'))
689 os.remove(dst)
690
Antoine Pitrou78091e62011-12-29 18:54:15 +0100691 @support.skip_unless_symlink
692 def test_copyfile_symlinks(self):
693 tmp_dir = self.mkdtemp()
694 src = os.path.join(tmp_dir, 'src')
695 dst = os.path.join(tmp_dir, 'dst')
696 dst_link = os.path.join(tmp_dir, 'dst_link')
697 link = os.path.join(tmp_dir, 'link')
698 write_file(src, 'foo')
699 os.symlink(src, link)
700 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700701 shutil.copyfile(link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100702 self.assertTrue(os.path.islink(dst_link))
703 self.assertEqual(os.readlink(link), os.readlink(dst_link))
704 # follow
705 shutil.copyfile(link, dst)
706 self.assertFalse(os.path.islink(dst))
707
Hynek Schlawack2100b422012-06-23 20:28:32 +0200708 def test_rmtree_uses_safe_fd_version_if_available(self):
Hynek Schlawackd0f6e0a2012-06-29 08:28:20 +0200709 _use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
710 os.supports_dir_fd and
711 os.listdir in os.supports_fd and
712 os.stat in os.supports_follow_symlinks)
713 if _use_fd_functions:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200714 self.assertTrue(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000715 self.assertTrue(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200716 tmp_dir = self.mkdtemp()
717 d = os.path.join(tmp_dir, 'a')
718 os.mkdir(d)
719 try:
720 real_rmtree = shutil._rmtree_safe_fd
721 class Called(Exception): pass
722 def _raiser(*args, **kwargs):
723 raise Called
724 shutil._rmtree_safe_fd = _raiser
725 self.assertRaises(Called, shutil.rmtree, d)
726 finally:
727 shutil._rmtree_safe_fd = real_rmtree
728 else:
729 self.assertFalse(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000730 self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200731
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000732 def test_rmtree_dont_delete_file(self):
733 # When called on a file instead of a directory, don't delete it.
734 handle, path = tempfile.mkstemp()
Victor Stinnerbf816222011-06-30 23:25:47 +0200735 os.close(handle)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200736 self.assertRaises(NotADirectoryError, shutil.rmtree, path)
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000737 os.remove(path)
738
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000739 def test_copytree_simple(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000740 src_dir = tempfile.mkdtemp()
741 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200742 self.addCleanup(shutil.rmtree, src_dir)
743 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
744 write_file((src_dir, 'test.txt'), '123')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000745 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200746 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000747
Éric Araujoa7e33a12011-08-12 19:51:35 +0200748 shutil.copytree(src_dir, dst_dir)
749 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test.txt')))
750 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'test_dir')))
751 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test_dir',
752 'test.txt')))
753 actual = read_file((dst_dir, 'test.txt'))
754 self.assertEqual(actual, '123')
755 actual = read_file((dst_dir, 'test_dir', 'test.txt'))
756 self.assertEqual(actual, '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000757
jab9e00d9e2018-12-28 13:03:40 -0500758 def test_copytree_dirs_exist_ok(self):
759 src_dir = tempfile.mkdtemp()
760 dst_dir = tempfile.mkdtemp()
761 self.addCleanup(shutil.rmtree, src_dir)
762 self.addCleanup(shutil.rmtree, dst_dir)
763
764 write_file((src_dir, 'nonexisting.txt'), '123')
765 os.mkdir(os.path.join(src_dir, 'existing_dir'))
766 os.mkdir(os.path.join(dst_dir, 'existing_dir'))
767 write_file((dst_dir, 'existing_dir', 'existing.txt'), 'will be replaced')
768 write_file((src_dir, 'existing_dir', 'existing.txt'), 'has been replaced')
769
770 shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)
771 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'nonexisting.txt')))
772 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'existing_dir')))
773 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'existing_dir',
774 'existing.txt')))
775 actual = read_file((dst_dir, 'nonexisting.txt'))
776 self.assertEqual(actual, '123')
777 actual = read_file((dst_dir, 'existing_dir', 'existing.txt'))
778 self.assertEqual(actual, 'has been replaced')
779
780 with self.assertRaises(FileExistsError):
781 shutil.copytree(src_dir, dst_dir, dirs_exist_ok=False)
782
Antoine Pitrou78091e62011-12-29 18:54:15 +0100783 @support.skip_unless_symlink
784 def test_copytree_symlinks(self):
785 tmp_dir = self.mkdtemp()
786 src_dir = os.path.join(tmp_dir, 'src')
787 dst_dir = os.path.join(tmp_dir, 'dst')
788 sub_dir = os.path.join(src_dir, 'sub')
789 os.mkdir(src_dir)
790 os.mkdir(sub_dir)
791 write_file((src_dir, 'file.txt'), 'foo')
792 src_link = os.path.join(sub_dir, 'link')
793 dst_link = os.path.join(dst_dir, 'sub/link')
794 os.symlink(os.path.join(src_dir, 'file.txt'),
795 src_link)
796 if hasattr(os, 'lchmod'):
797 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
798 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
799 os.lchflags(src_link, stat.UF_NODUMP)
800 src_stat = os.lstat(src_link)
801 shutil.copytree(src_dir, dst_dir, symlinks=True)
802 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'sub', 'link')))
Steve Dower9eb3d542019-08-21 15:52:42 -0700803 actual = os.readlink(os.path.join(dst_dir, 'sub', 'link'))
804 # Bad practice to blindly strip the prefix as it may be required to
805 # correctly refer to the file, but we're only comparing paths here.
806 if os.name == 'nt' and actual.startswith('\\\\?\\'):
807 actual = actual[4:]
808 self.assertEqual(actual, os.path.join(src_dir, 'file.txt'))
Antoine Pitrou78091e62011-12-29 18:54:15 +0100809 dst_stat = os.lstat(dst_link)
810 if hasattr(os, 'lchmod'):
811 self.assertEqual(dst_stat.st_mode, src_stat.st_mode)
812 if hasattr(os, 'lchflags'):
813 self.assertEqual(dst_stat.st_flags, src_stat.st_flags)
814
Georg Brandl2ee470f2008-07-16 12:55:28 +0000815 def test_copytree_with_exclude(self):
Georg Brandl2ee470f2008-07-16 12:55:28 +0000816 # creating data
817 join = os.path.join
818 exists = os.path.exists
819 src_dir = tempfile.mkdtemp()
Georg Brandl2ee470f2008-07-16 12:55:28 +0000820 try:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000821 dst_dir = join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200822 write_file((src_dir, 'test.txt'), '123')
823 write_file((src_dir, 'test.tmp'), '123')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000824 os.mkdir(join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200825 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000826 os.mkdir(join(src_dir, 'test_dir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200827 write_file((src_dir, 'test_dir2', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000828 os.mkdir(join(src_dir, 'test_dir2', 'subdir'))
829 os.mkdir(join(src_dir, 'test_dir2', 'subdir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200830 write_file((src_dir, 'test_dir2', 'subdir', 'test.txt'), '456')
831 write_file((src_dir, 'test_dir2', 'subdir2', 'test.py'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000832
833 # testing glob-like patterns
834 try:
835 patterns = shutil.ignore_patterns('*.tmp', 'test_dir2')
836 shutil.copytree(src_dir, dst_dir, ignore=patterns)
837 # checking the result: some elements should not be copied
838 self.assertTrue(exists(join(dst_dir, 'test.txt')))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200839 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
840 self.assertFalse(exists(join(dst_dir, 'test_dir2')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000841 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200842 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000843 try:
844 patterns = shutil.ignore_patterns('*.tmp', 'subdir*')
845 shutil.copytree(src_dir, dst_dir, ignore=patterns)
846 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200847 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
848 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2')))
849 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000850 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200851 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000852
853 # testing callable-style
854 try:
855 def _filter(src, names):
856 res = []
857 for name in names:
858 path = os.path.join(src, name)
859
860 if (os.path.isdir(path) and
861 path.split()[-1] == 'subdir'):
862 res.append(name)
863 elif os.path.splitext(path)[-1] in ('.py'):
864 res.append(name)
865 return res
866
867 shutil.copytree(src_dir, dst_dir, ignore=_filter)
868
869 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200870 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2',
871 'test.py')))
872 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000873
874 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200875 shutil.rmtree(dst_dir)
Georg Brandl2ee470f2008-07-16 12:55:28 +0000876 finally:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000877 shutil.rmtree(src_dir)
878 shutil.rmtree(os.path.dirname(dst_dir))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000879
Antoine Pitrouac601602013-08-16 19:35:02 +0200880 def test_copytree_retains_permissions(self):
881 tmp_dir = tempfile.mkdtemp()
882 src_dir = os.path.join(tmp_dir, 'source')
883 os.mkdir(src_dir)
884 dst_dir = os.path.join(tmp_dir, 'destination')
885 self.addCleanup(shutil.rmtree, tmp_dir)
886
887 os.chmod(src_dir, 0o777)
888 write_file((src_dir, 'permissive.txt'), '123')
889 os.chmod(os.path.join(src_dir, 'permissive.txt'), 0o777)
890 write_file((src_dir, 'restrictive.txt'), '456')
891 os.chmod(os.path.join(src_dir, 'restrictive.txt'), 0o600)
892 restrictive_subdir = tempfile.mkdtemp(dir=src_dir)
893 os.chmod(restrictive_subdir, 0o600)
894
895 shutil.copytree(src_dir, dst_dir)
Brett Cannon9c7eb552013-08-23 14:38:11 -0400896 self.assertEqual(os.stat(src_dir).st_mode, os.stat(dst_dir).st_mode)
897 self.assertEqual(os.stat(os.path.join(src_dir, 'permissive.txt')).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200898 os.stat(os.path.join(dst_dir, 'permissive.txt')).st_mode)
Brett Cannon9c7eb552013-08-23 14:38:11 -0400899 self.assertEqual(os.stat(os.path.join(src_dir, 'restrictive.txt')).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200900 os.stat(os.path.join(dst_dir, 'restrictive.txt')).st_mode)
901 restrictive_subdir_dst = os.path.join(dst_dir,
902 os.path.split(restrictive_subdir)[1])
Brett Cannon9c7eb552013-08-23 14:38:11 -0400903 self.assertEqual(os.stat(restrictive_subdir).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200904 os.stat(restrictive_subdir_dst).st_mode)
905
Berker Peksag884afd92014-12-10 02:50:32 +0200906 @unittest.mock.patch('os.chmod')
907 def test_copytree_winerror(self, mock_patch):
908 # When copying to VFAT, copystat() raises OSError. On Windows, the
909 # exception object has a meaningful 'winerror' attribute, but not
910 # on other operating systems. Do not assume 'winerror' is set.
911 src_dir = tempfile.mkdtemp()
912 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
913 self.addCleanup(shutil.rmtree, src_dir)
914 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
915
916 mock_patch.side_effect = PermissionError('ka-boom')
917 with self.assertRaises(shutil.Error):
918 shutil.copytree(src_dir, dst_dir)
919
Giampaolo Rodolac606a9c2019-02-26 12:04:41 +0100920 def test_copytree_custom_copy_function(self):
921 # See: https://bugs.python.org/issue35648
922 def custom_cpfun(a, b):
923 flag.append(None)
924 self.assertIsInstance(a, str)
925 self.assertIsInstance(b, str)
926 self.assertEqual(a, os.path.join(src, 'foo'))
927 self.assertEqual(b, os.path.join(dst, 'foo'))
928
929 flag = []
930 src = tempfile.mkdtemp()
Miss Islington (bot)7fe81ce2019-06-26 16:57:49 -0700931 self.addCleanup(support.rmtree, src)
Giampaolo Rodolac606a9c2019-02-26 12:04:41 +0100932 dst = tempfile.mktemp()
Miss Islington (bot)7fe81ce2019-06-26 16:57:49 -0700933 self.addCleanup(support.rmtree, dst)
Giampaolo Rodolac606a9c2019-02-26 12:04:41 +0100934 with open(os.path.join(src, 'foo'), 'w') as f:
935 f.close()
936 shutil.copytree(src, dst, copy_function=custom_cpfun)
937 self.assertEqual(len(flag), 1)
938
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000939 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000940 def test_dont_copy_file_onto_link_to_itself(self):
941 # bug 851123.
942 os.mkdir(TESTFN)
943 src = os.path.join(TESTFN, 'cheese')
944 dst = os.path.join(TESTFN, 'shop')
945 try:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000946 with open(src, 'w') as f:
947 f.write('cheddar')
xdegaye92c2ca72017-11-12 17:31:07 +0100948 try:
949 os.link(src, dst)
950 except PermissionError as e:
951 self.skipTest('os.link(): %s' % e)
Hynek Schlawack48653762012-10-07 12:49:58 +0200952 self.assertRaises(shutil.SameFileError, shutil.copyfile, src, dst)
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000953 with open(src, 'r') as f:
954 self.assertEqual(f.read(), 'cheddar')
955 os.remove(dst)
956 finally:
957 shutil.rmtree(TESTFN, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000958
Brian Curtin3b4499c2010-12-28 14:31:47 +0000959 @support.skip_unless_symlink
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000960 def test_dont_copy_file_onto_symlink_to_itself(self):
961 # bug 851123.
962 os.mkdir(TESTFN)
963 src = os.path.join(TESTFN, 'cheese')
964 dst = os.path.join(TESTFN, 'shop')
965 try:
966 with open(src, 'w') as f:
967 f.write('cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000968 # Using `src` here would mean we end up with a symlink pointing
969 # to TESTFN/TESTFN/cheese, while it should point at
970 # TESTFN/cheese.
971 os.symlink('cheese', dst)
Hynek Schlawack48653762012-10-07 12:49:58 +0200972 self.assertRaises(shutil.SameFileError, shutil.copyfile, src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000973 with open(src, 'r') as f:
974 self.assertEqual(f.read(), 'cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000975 os.remove(dst)
976 finally:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000977 shutil.rmtree(TESTFN, ignore_errors=True)
Johannes Gijsbers68128712004-08-14 13:57:08 +0000978
Brian Curtin3b4499c2010-12-28 14:31:47 +0000979 @support.skip_unless_symlink
Brian Curtind40e6f72010-07-08 21:39:08 +0000980 def test_rmtree_on_symlink(self):
981 # bug 1669.
982 os.mkdir(TESTFN)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000983 try:
Brian Curtind40e6f72010-07-08 21:39:08 +0000984 src = os.path.join(TESTFN, 'cheese')
985 dst = os.path.join(TESTFN, 'shop')
986 os.mkdir(src)
987 os.symlink(src, dst)
988 self.assertRaises(OSError, shutil.rmtree, dst)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200989 shutil.rmtree(dst, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000990 finally:
Brian Curtind40e6f72010-07-08 21:39:08 +0000991 shutil.rmtree(TESTFN, ignore_errors=True)
992
Steve Dower9eb3d542019-08-21 15:52:42 -0700993 @unittest.skipUnless(_winapi, 'only relevant on Windows')
994 def test_rmtree_on_junction(self):
995 os.mkdir(TESTFN)
996 try:
997 src = os.path.join(TESTFN, 'cheese')
998 dst = os.path.join(TESTFN, 'shop')
999 os.mkdir(src)
1000 open(os.path.join(src, 'spam'), 'wb').close()
1001 _winapi.CreateJunction(src, dst)
1002 self.assertRaises(OSError, shutil.rmtree, dst)
1003 shutil.rmtree(dst, ignore_errors=True)
1004 finally:
1005 shutil.rmtree(TESTFN, ignore_errors=True)
1006
Serhiy Storchaka43767632013-11-03 21:31:38 +02001007 # Issue #3002: copyfile and copytree block indefinitely on named pipes
1008 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
1009 def test_copyfile_named_pipe(self):
xdegaye92c2ca72017-11-12 17:31:07 +01001010 try:
1011 os.mkfifo(TESTFN)
1012 except PermissionError as e:
1013 self.skipTest('os.mkfifo(): %s' % e)
Serhiy Storchaka43767632013-11-03 21:31:38 +02001014 try:
1015 self.assertRaises(shutil.SpecialFileError,
1016 shutil.copyfile, TESTFN, TESTFN2)
1017 self.assertRaises(shutil.SpecialFileError,
1018 shutil.copyfile, __file__, TESTFN)
1019 finally:
1020 os.remove(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +00001021
Serhiy Storchaka43767632013-11-03 21:31:38 +02001022 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
1023 @support.skip_unless_symlink
1024 def test_copytree_named_pipe(self):
1025 os.mkdir(TESTFN)
1026 try:
1027 subdir = os.path.join(TESTFN, "subdir")
1028 os.mkdir(subdir)
1029 pipe = os.path.join(subdir, "mypipe")
xdegaye92c2ca72017-11-12 17:31:07 +01001030 try:
1031 os.mkfifo(pipe)
1032 except PermissionError as e:
1033 self.skipTest('os.mkfifo(): %s' % e)
Antoine Pitrou7fff0962009-05-01 21:09:44 +00001034 try:
Serhiy Storchaka43767632013-11-03 21:31:38 +02001035 shutil.copytree(TESTFN, TESTFN2)
1036 except shutil.Error as e:
1037 errors = e.args[0]
1038 self.assertEqual(len(errors), 1)
1039 src, dst, error_msg = errors[0]
1040 self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
1041 else:
1042 self.fail("shutil.Error should have been raised")
1043 finally:
1044 shutil.rmtree(TESTFN, ignore_errors=True)
1045 shutil.rmtree(TESTFN2, ignore_errors=True)
Antoine Pitrou7fff0962009-05-01 21:09:44 +00001046
Tarek Ziadé5340db32010-04-19 22:30:51 +00001047 def test_copytree_special_func(self):
1048
1049 src_dir = self.mkdtemp()
1050 dst_dir = os.path.join(self.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +02001051 write_file((src_dir, 'test.txt'), '123')
Tarek Ziadé5340db32010-04-19 22:30:51 +00001052 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +02001053 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadé5340db32010-04-19 22:30:51 +00001054
1055 copied = []
1056 def _copy(src, dst):
1057 copied.append((src, dst))
1058
1059 shutil.copytree(src_dir, dst_dir, copy_function=_copy)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001060 self.assertEqual(len(copied), 2)
Tarek Ziadé5340db32010-04-19 22:30:51 +00001061
Brian Curtin3b4499c2010-12-28 14:31:47 +00001062 @support.skip_unless_symlink
Tarek Ziadéfb437512010-04-20 08:57:33 +00001063 def test_copytree_dangling_symlinks(self):
1064
1065 # a dangling symlink raises an error at the end
1066 src_dir = self.mkdtemp()
1067 dst_dir = os.path.join(self.mkdtemp(), 'destination')
1068 os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt'))
1069 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +02001070 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadéfb437512010-04-20 08:57:33 +00001071 self.assertRaises(Error, shutil.copytree, src_dir, dst_dir)
1072
1073 # a dangling symlink is ignored with the proper flag
1074 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
1075 shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)
1076 self.assertNotIn('test.txt', os.listdir(dst_dir))
1077
1078 # a dangling symlink is copied if symlinks=True
1079 dst_dir = os.path.join(self.mkdtemp(), 'destination3')
1080 shutil.copytree(src_dir, dst_dir, symlinks=True)
1081 self.assertIn('test.txt', os.listdir(dst_dir))
1082
Berker Peksag5a294d82015-07-25 14:53:48 +03001083 @support.skip_unless_symlink
1084 def test_copytree_symlink_dir(self):
1085 src_dir = self.mkdtemp()
1086 dst_dir = os.path.join(self.mkdtemp(), 'destination')
1087 os.mkdir(os.path.join(src_dir, 'real_dir'))
1088 with open(os.path.join(src_dir, 'real_dir', 'test.txt'), 'w'):
1089 pass
1090 os.symlink(os.path.join(src_dir, 'real_dir'),
1091 os.path.join(src_dir, 'link_to_dir'),
1092 target_is_directory=True)
1093
1094 shutil.copytree(src_dir, dst_dir, symlinks=False)
1095 self.assertFalse(os.path.islink(os.path.join(dst_dir, 'link_to_dir')))
1096 self.assertIn('test.txt', os.listdir(os.path.join(dst_dir, 'link_to_dir')))
1097
1098 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
1099 shutil.copytree(src_dir, dst_dir, symlinks=True)
1100 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'link_to_dir')))
1101 self.assertIn('test.txt', os.listdir(os.path.join(dst_dir, 'link_to_dir')))
1102
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001103 def _copy_file(self, method):
1104 fname = 'test.txt'
1105 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +02001106 write_file((tmpdir, fname), 'xxx')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001107 file1 = os.path.join(tmpdir, fname)
1108 tmpdir2 = self.mkdtemp()
1109 method(file1, tmpdir2)
1110 file2 = os.path.join(tmpdir2, fname)
1111 return (file1, file2)
1112
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001113 def test_copy(self):
1114 # Ensure that the copied file exists and has the same mode bits.
1115 file1, file2 = self._copy_file(shutil.copy)
1116 self.assertTrue(os.path.exists(file2))
1117 self.assertEqual(os.stat(file1).st_mode, os.stat(file2).st_mode)
1118
Senthil Kumaran0c2dba52011-07-03 18:21:38 -07001119 @unittest.skipUnless(hasattr(os, 'utime'), 'requires os.utime')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001120 def test_copy2(self):
1121 # Ensure that the copied file exists and has the same mode and
1122 # modification time bits.
1123 file1, file2 = self._copy_file(shutil.copy2)
1124 self.assertTrue(os.path.exists(file2))
1125 file1_stat = os.stat(file1)
1126 file2_stat = os.stat(file2)
1127 self.assertEqual(file1_stat.st_mode, file2_stat.st_mode)
1128 for attr in 'st_atime', 'st_mtime':
1129 # The modification times may be truncated in the new file.
1130 self.assertLessEqual(getattr(file1_stat, attr),
1131 getattr(file2_stat, attr) + 1)
1132 if hasattr(os, 'chflags') and hasattr(file1_stat, 'st_flags'):
1133 self.assertEqual(getattr(file1_stat, 'st_flags'),
1134 getattr(file2_stat, 'st_flags'))
1135
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001136 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001137 def test_make_tarball(self):
1138 # creating something to tar
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001139 root_dir, base_dir = self._create_files('')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001140
1141 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001142 # force shutil to create the directory
1143 os.rmdir(tmpdir2)
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001144 # working with relative paths
1145 work_dir = os.path.dirname(tmpdir2)
1146 rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001147
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001148 with support.change_cwd(work_dir):
Serhiy Storchaka5558d4f2015-09-08 09:59:02 +03001149 base_name = os.path.abspath(rel_base_name)
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001150 tarball = make_archive(rel_base_name, 'gztar', root_dir, '.')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001151
1152 # check if the compressed tarball was created
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001153 self.assertEqual(tarball, base_name + '.tar.gz')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001154 self.assertTrue(os.path.isfile(tarball))
1155 self.assertTrue(tarfile.is_tarfile(tarball))
1156 with tarfile.open(tarball, 'r:gz') as tf:
1157 self.assertCountEqual(tf.getnames(),
1158 ['.', './sub', './sub2',
1159 './file1', './file2', './sub/file3'])
Tarek Ziadé396fad72010-02-23 05:30:31 +00001160
1161 # trying an uncompressed one
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001162 with support.change_cwd(work_dir):
1163 tarball = make_archive(rel_base_name, 'tar', root_dir, '.')
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001164 self.assertEqual(tarball, base_name + '.tar')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001165 self.assertTrue(os.path.isfile(tarball))
1166 self.assertTrue(tarfile.is_tarfile(tarball))
1167 with tarfile.open(tarball, 'r') as tf:
1168 self.assertCountEqual(tf.getnames(),
1169 ['.', './sub', './sub2',
1170 './file1', './file2', './sub/file3'])
Tarek Ziadé396fad72010-02-23 05:30:31 +00001171
1172 def _tarinfo(self, path):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001173 with tarfile.open(path) as tar:
Tarek Ziadé396fad72010-02-23 05:30:31 +00001174 names = tar.getnames()
1175 names.sort()
1176 return tuple(names)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001177
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001178 def _create_files(self, base_dir='dist'):
Tarek Ziadé396fad72010-02-23 05:30:31 +00001179 # creating something to tar
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001180 root_dir = self.mkdtemp()
1181 dist = os.path.join(root_dir, base_dir)
1182 os.makedirs(dist, exist_ok=True)
Éric Araujoa7e33a12011-08-12 19:51:35 +02001183 write_file((dist, 'file1'), 'xxx')
1184 write_file((dist, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001185 os.mkdir(os.path.join(dist, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +02001186 write_file((dist, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001187 os.mkdir(os.path.join(dist, 'sub2'))
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001188 if base_dir:
1189 write_file((root_dir, 'outer'), 'xxx')
1190 return root_dir, base_dir
Tarek Ziadé396fad72010-02-23 05:30:31 +00001191
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001192 @support.requires_zlib
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001193 @unittest.skipUnless(shutil.which('tar'),
Tarek Ziadé396fad72010-02-23 05:30:31 +00001194 'Need the tar command to run')
1195 def test_tarfile_vs_tar(self):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001196 root_dir, base_dir = self._create_files()
1197 base_name = os.path.join(self.mkdtemp(), 'archive')
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001198 tarball = make_archive(base_name, 'gztar', root_dir, base_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001199
1200 # check if the compressed tarball was created
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001201 self.assertEqual(tarball, base_name + '.tar.gz')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001202 self.assertTrue(os.path.isfile(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001203
1204 # now create another tarball using `tar`
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001205 tarball2 = os.path.join(root_dir, 'archive2.tar')
1206 tar_cmd = ['tar', '-cf', 'archive2.tar', base_dir]
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001207 subprocess.check_call(tar_cmd, cwd=root_dir,
1208 stdout=subprocess.DEVNULL)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001209
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001210 self.assertTrue(os.path.isfile(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001211 # let's compare both tarballs
Ezio Melottib3aedd42010-11-20 19:04:17 +00001212 self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001213
1214 # trying an uncompressed one
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001215 tarball = make_archive(base_name, 'tar', root_dir, base_dir)
1216 self.assertEqual(tarball, base_name + '.tar')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001217 self.assertTrue(os.path.isfile(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001218
1219 # now for a dry_run
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001220 tarball = make_archive(base_name, 'tar', root_dir, base_dir,
1221 dry_run=True)
1222 self.assertEqual(tarball, base_name + '.tar')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001223 self.assertTrue(os.path.isfile(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001224
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001225 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001226 def test_make_zipfile(self):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001227 # creating something to zip
1228 root_dir, base_dir = self._create_files()
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001229
1230 tmpdir2 = self.mkdtemp()
1231 # force shutil to create the directory
1232 os.rmdir(tmpdir2)
1233 # working with relative paths
1234 work_dir = os.path.dirname(tmpdir2)
1235 rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive')
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001236
1237 with support.change_cwd(work_dir):
Serhiy Storchaka5558d4f2015-09-08 09:59:02 +03001238 base_name = os.path.abspath(rel_base_name)
Serhiy Storchaka666de772016-10-23 15:55:09 +03001239 res = make_archive(rel_base_name, 'zip', root_dir)
1240
1241 self.assertEqual(res, base_name + '.zip')
1242 self.assertTrue(os.path.isfile(res))
1243 self.assertTrue(zipfile.is_zipfile(res))
1244 with zipfile.ZipFile(res) as zf:
1245 self.assertCountEqual(zf.namelist(),
1246 ['dist/', 'dist/sub/', 'dist/sub2/',
1247 'dist/file1', 'dist/file2', 'dist/sub/file3',
1248 'outer'])
1249
1250 with support.change_cwd(work_dir):
1251 base_name = os.path.abspath(rel_base_name)
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001252 res = make_archive(rel_base_name, 'zip', root_dir, base_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001253
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001254 self.assertEqual(res, base_name + '.zip')
1255 self.assertTrue(os.path.isfile(res))
1256 self.assertTrue(zipfile.is_zipfile(res))
1257 with zipfile.ZipFile(res) as zf:
1258 self.assertCountEqual(zf.namelist(),
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001259 ['dist/', 'dist/sub/', 'dist/sub2/',
1260 'dist/file1', 'dist/file2', 'dist/sub/file3'])
Tarek Ziadé396fad72010-02-23 05:30:31 +00001261
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001262 @support.requires_zlib
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001263 @unittest.skipUnless(shutil.which('zip'),
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001264 'Need the zip command to run')
1265 def test_zipfile_vs_zip(self):
1266 root_dir, base_dir = self._create_files()
1267 base_name = os.path.join(self.mkdtemp(), 'archive')
1268 archive = make_archive(base_name, 'zip', root_dir, base_dir)
1269
1270 # check if ZIP file was created
1271 self.assertEqual(archive, base_name + '.zip')
1272 self.assertTrue(os.path.isfile(archive))
1273
1274 # now create another ZIP file using `zip`
1275 archive2 = os.path.join(root_dir, 'archive2.zip')
1276 zip_cmd = ['zip', '-q', '-r', 'archive2.zip', base_dir]
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001277 subprocess.check_call(zip_cmd, cwd=root_dir,
1278 stdout=subprocess.DEVNULL)
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001279
1280 self.assertTrue(os.path.isfile(archive2))
1281 # let's compare both ZIP files
1282 with zipfile.ZipFile(archive) as zf:
1283 names = zf.namelist()
1284 with zipfile.ZipFile(archive2) as zf:
1285 names2 = zf.namelist()
1286 self.assertEqual(sorted(names), sorted(names2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001287
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001288 @support.requires_zlib
Serhiy Storchaka8bc792a2015-11-22 14:49:58 +02001289 @unittest.skipUnless(shutil.which('unzip'),
1290 'Need the unzip command to run')
1291 def test_unzip_zipfile(self):
1292 root_dir, base_dir = self._create_files()
1293 base_name = os.path.join(self.mkdtemp(), 'archive')
1294 archive = make_archive(base_name, 'zip', root_dir, base_dir)
1295
1296 # check if ZIP file was created
1297 self.assertEqual(archive, base_name + '.zip')
1298 self.assertTrue(os.path.isfile(archive))
1299
1300 # now check the ZIP file using `unzip -t`
1301 zip_cmd = ['unzip', '-t', archive]
1302 with support.change_cwd(root_dir):
1303 try:
1304 subprocess.check_output(zip_cmd, stderr=subprocess.STDOUT)
1305 except subprocess.CalledProcessError as exc:
1306 details = exc.output.decode(errors="replace")
Benjamin Petersona710ebd2018-09-13 10:08:46 -07001307 if 'unrecognized option: t' in details:
Benjamin Petersone78734d2018-09-13 10:57:23 -07001308 self.skipTest("unzip doesn't support -t")
Serhiy Storchaka8bc792a2015-11-22 14:49:58 +02001309 msg = "{}\n\n**Unzip Output**\n{}"
1310 self.fail(msg.format(exc, details))
1311
Tarek Ziadé396fad72010-02-23 05:30:31 +00001312 def test_make_archive(self):
1313 tmpdir = self.mkdtemp()
1314 base_name = os.path.join(tmpdir, 'archive')
1315 self.assertRaises(ValueError, make_archive, base_name, 'xxx')
1316
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001317 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001318 def test_make_archive_owner_group(self):
1319 # testing make_archive with owner and group, with various combinations
1320 # this works even if there's not gid/uid support
1321 if UID_GID_SUPPORT:
1322 group = grp.getgrgid(0)[0]
1323 owner = pwd.getpwuid(0)[0]
1324 else:
1325 group = owner = 'root'
1326
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001327 root_dir, base_dir = self._create_files()
1328 base_name = os.path.join(self.mkdtemp(), 'archive')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001329 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
1330 group=group)
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001331 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001332
1333 res = make_archive(base_name, 'zip', root_dir, base_dir)
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001334 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001335
1336 res = make_archive(base_name, 'tar', root_dir, base_dir,
1337 owner=owner, group=group)
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001338 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001339
1340 res = make_archive(base_name, 'tar', root_dir, base_dir,
1341 owner='kjhkjhkjg', group='oihohoh')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001342 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001343
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001344
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001345 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001346 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1347 def test_tarfile_root_owner(self):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001348 root_dir, base_dir = self._create_files()
1349 base_name = os.path.join(self.mkdtemp(), 'archive')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001350 group = grp.getgrgid(0)[0]
1351 owner = pwd.getpwuid(0)[0]
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001352 with support.change_cwd(root_dir):
1353 archive_name = make_archive(base_name, 'gztar', root_dir, 'dist',
1354 owner=owner, group=group)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001355
1356 # check if the compressed tarball was created
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001357 self.assertTrue(os.path.isfile(archive_name))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001358
1359 # now checks the rights
1360 archive = tarfile.open(archive_name)
1361 try:
1362 for member in archive.getmembers():
Ezio Melottib3aedd42010-11-20 19:04:17 +00001363 self.assertEqual(member.uid, 0)
1364 self.assertEqual(member.gid, 0)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001365 finally:
1366 archive.close()
1367
1368 def test_make_archive_cwd(self):
1369 current_dir = os.getcwd()
1370 def _breaks(*args, **kw):
1371 raise RuntimeError()
1372
1373 register_archive_format('xxx', _breaks, [], 'xxx file')
1374 try:
1375 try:
1376 make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
1377 except Exception:
1378 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +00001379 self.assertEqual(os.getcwd(), current_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001380 finally:
1381 unregister_archive_format('xxx')
1382
Serhiy Storchaka9a4fc192014-11-28 00:48:46 +02001383 def test_make_tarfile_in_curdir(self):
1384 # Issue #21280
1385 root_dir = self.mkdtemp()
1386 with support.change_cwd(root_dir):
1387 self.assertEqual(make_archive('test', 'tar'), 'test.tar')
1388 self.assertTrue(os.path.isfile('test.tar'))
1389
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001390 @support.requires_zlib
Serhiy Storchaka9a4fc192014-11-28 00:48:46 +02001391 def test_make_zipfile_in_curdir(self):
1392 # Issue #21280
1393 root_dir = self.mkdtemp()
1394 with support.change_cwd(root_dir):
1395 self.assertEqual(make_archive('test', 'zip'), 'test.zip')
1396 self.assertTrue(os.path.isfile('test.zip'))
1397
Tarek Ziadé396fad72010-02-23 05:30:31 +00001398 def test_register_archive_format(self):
1399
1400 self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
1401 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1402 1)
1403 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1404 [(1, 2), (1, 2, 3)])
1405
1406 register_archive_format('xxx', lambda: x, [(1, 2)], 'xxx file')
1407 formats = [name for name, params in get_archive_formats()]
1408 self.assertIn('xxx', formats)
1409
1410 unregister_archive_format('xxx')
1411 formats = [name for name, params in get_archive_formats()]
1412 self.assertNotIn('xxx', formats)
1413
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001414 def check_unpack_archive(self, format):
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001415 self.check_unpack_archive_with_converter(format, lambda path: path)
1416 self.check_unpack_archive_with_converter(format, pathlib.Path)
Serhiy Storchakab21d1552018-03-02 11:53:51 +02001417 self.check_unpack_archive_with_converter(format, FakePath)
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001418
1419 def check_unpack_archive_with_converter(self, format, converter):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001420 root_dir, base_dir = self._create_files()
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001421 expected = rlistdir(root_dir)
1422 expected.remove('outer')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001423
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001424 base_name = os.path.join(self.mkdtemp(), 'archive')
1425 filename = make_archive(base_name, format, root_dir, base_dir)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001426
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001427 # let's try to unpack it now
1428 tmpdir2 = self.mkdtemp()
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001429 unpack_archive(converter(filename), converter(tmpdir2))
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001430 self.assertEqual(rlistdir(tmpdir2), expected)
1431
1432 # and again, this time with the format specified
1433 tmpdir3 = self.mkdtemp()
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001434 unpack_archive(converter(filename), converter(tmpdir3), format=format)
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001435 self.assertEqual(rlistdir(tmpdir3), expected)
1436
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001437 self.assertRaises(shutil.ReadError, unpack_archive, converter(TESTFN))
1438 self.assertRaises(ValueError, unpack_archive, converter(TESTFN), format='xxx')
Nick Coghlanabf202d2011-03-16 13:52:20 -04001439
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001440 def test_unpack_archive_tar(self):
1441 self.check_unpack_archive('tar')
1442
1443 @support.requires_zlib
1444 def test_unpack_archive_gztar(self):
1445 self.check_unpack_archive('gztar')
1446
1447 @support.requires_bz2
1448 def test_unpack_archive_bztar(self):
1449 self.check_unpack_archive('bztar')
1450
1451 @support.requires_lzma
Michael Feltef110b12019-02-18 12:02:44 +01001452 @unittest.skipIf(AIX and not _maxdataOK(), "AIX MAXDATA must be 0x20000000 or larger")
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001453 def test_unpack_archive_xztar(self):
1454 self.check_unpack_archive('xztar')
1455
1456 @support.requires_zlib
1457 def test_unpack_archive_zip(self):
1458 self.check_unpack_archive('zip')
1459
Martin Pantereb995702016-07-28 01:11:04 +00001460 def test_unpack_registry(self):
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001461
1462 formats = get_unpack_formats()
1463
1464 def _boo(filename, extract_dir, extra):
Ezio Melottib3aedd42010-11-20 19:04:17 +00001465 self.assertEqual(extra, 1)
1466 self.assertEqual(filename, 'stuff.boo')
1467 self.assertEqual(extract_dir, 'xx')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001468
1469 register_unpack_format('Boo', ['.boo', '.b2'], _boo, [('extra', 1)])
1470 unpack_archive('stuff.boo', 'xx')
1471
1472 # trying to register a .boo unpacker again
1473 self.assertRaises(RegistryError, register_unpack_format, 'Boo2',
1474 ['.boo'], _boo)
1475
1476 # should work now
1477 unregister_unpack_format('Boo')
1478 register_unpack_format('Boo2', ['.boo'], _boo)
1479 self.assertIn(('Boo2', ['.boo'], ''), get_unpack_formats())
1480 self.assertNotIn(('Boo', ['.boo'], ''), get_unpack_formats())
1481
1482 # let's leave a clean state
1483 unregister_unpack_format('Boo2')
Ezio Melottib3aedd42010-11-20 19:04:17 +00001484 self.assertEqual(get_unpack_formats(), formats)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001485
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001486 @unittest.skipUnless(hasattr(shutil, 'disk_usage'),
1487 "disk_usage not available on this platform")
1488 def test_disk_usage(self):
Gregory P. Smith529746c2017-07-06 17:11:27 -07001489 usage = shutil.disk_usage(os.path.dirname(__file__))
Victor Stinnerdc525f42018-12-11 12:05:21 +01001490 for attr in ('total', 'used', 'free'):
1491 self.assertIsInstance(getattr(usage, attr), int)
Éric Araujo2ee61882011-07-02 16:45:45 +02001492 self.assertGreater(usage.total, 0)
1493 self.assertGreater(usage.used, 0)
1494 self.assertGreaterEqual(usage.free, 0)
1495 self.assertGreaterEqual(usage.total, usage.used)
1496 self.assertGreater(usage.total, usage.free)
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001497
Victor Stinnerdc525f42018-12-11 12:05:21 +01001498 # bpo-32557: Check that disk_usage() also accepts a filename
1499 shutil.disk_usage(__file__)
1500
Sandro Tosid902a142011-08-22 23:28:27 +02001501 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1502 @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
1503 def test_chown(self):
1504
1505 # cleaned-up automatically by TestShutil.tearDown method
1506 dirname = self.mkdtemp()
1507 filename = tempfile.mktemp(dir=dirname)
1508 write_file(filename, 'testing chown function')
1509
1510 with self.assertRaises(ValueError):
1511 shutil.chown(filename)
1512
1513 with self.assertRaises(LookupError):
Raymond Hettinger15f44ab2016-08-30 10:47:49 -07001514 shutil.chown(filename, user='non-existing username')
Sandro Tosid902a142011-08-22 23:28:27 +02001515
1516 with self.assertRaises(LookupError):
Raymond Hettinger15f44ab2016-08-30 10:47:49 -07001517 shutil.chown(filename, group='non-existing groupname')
Sandro Tosid902a142011-08-22 23:28:27 +02001518
1519 with self.assertRaises(TypeError):
1520 shutil.chown(filename, b'spam')
1521
1522 with self.assertRaises(TypeError):
1523 shutil.chown(filename, 3.14)
1524
1525 uid = os.getuid()
1526 gid = os.getgid()
1527
1528 def check_chown(path, uid=None, gid=None):
1529 s = os.stat(filename)
1530 if uid is not None:
1531 self.assertEqual(uid, s.st_uid)
1532 if gid is not None:
1533 self.assertEqual(gid, s.st_gid)
1534
1535 shutil.chown(filename, uid, gid)
1536 check_chown(filename, uid, gid)
1537 shutil.chown(filename, uid)
1538 check_chown(filename, uid)
1539 shutil.chown(filename, user=uid)
1540 check_chown(filename, uid)
1541 shutil.chown(filename, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001542 check_chown(filename, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001543
1544 shutil.chown(dirname, uid, gid)
1545 check_chown(dirname, uid, gid)
1546 shutil.chown(dirname, uid)
1547 check_chown(dirname, uid)
1548 shutil.chown(dirname, user=uid)
1549 check_chown(dirname, uid)
1550 shutil.chown(dirname, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001551 check_chown(dirname, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001552
1553 user = pwd.getpwuid(uid)[0]
1554 group = grp.getgrgid(gid)[0]
1555 shutil.chown(filename, user, group)
1556 check_chown(filename, uid, gid)
1557 shutil.chown(dirname, user, group)
1558 check_chown(dirname, uid, gid)
1559
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001560 def test_copy_return_value(self):
1561 # copy and copy2 both return their destination path.
1562 for fn in (shutil.copy, shutil.copy2):
1563 src_dir = self.mkdtemp()
1564 dst_dir = self.mkdtemp()
1565 src = os.path.join(src_dir, 'foo')
1566 write_file(src, 'foo')
1567 rv = fn(src, dst_dir)
1568 self.assertEqual(rv, os.path.join(dst_dir, 'foo'))
1569 rv = fn(src, os.path.join(dst_dir, 'bar'))
1570 self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
1571
1572 def test_copyfile_return_value(self):
1573 # copytree returns its destination path.
1574 src_dir = self.mkdtemp()
1575 dst_dir = self.mkdtemp()
1576 dst_file = os.path.join(dst_dir, 'bar')
1577 src_file = os.path.join(src_dir, 'foo')
1578 write_file(src_file, 'foo')
1579 rv = shutil.copyfile(src_file, dst_file)
1580 self.assertTrue(os.path.exists(rv))
1581 self.assertEqual(read_file(src_file), read_file(dst_file))
1582
Hynek Schlawack48653762012-10-07 12:49:58 +02001583 def test_copyfile_same_file(self):
1584 # copyfile() should raise SameFileError if the source and destination
1585 # are the same.
1586 src_dir = self.mkdtemp()
1587 src_file = os.path.join(src_dir, 'foo')
1588 write_file(src_file, 'foo')
1589 self.assertRaises(SameFileError, shutil.copyfile, src_file, src_file)
Hynek Schlawack27ddb572012-10-28 13:59:27 +01001590 # But Error should work too, to stay backward compatible.
1591 self.assertRaises(Error, shutil.copyfile, src_file, src_file)
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02001592 # Make sure file is not corrupted.
1593 self.assertEqual(read_file(src_file), 'foo')
Hynek Schlawack48653762012-10-07 12:49:58 +02001594
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001595 def test_copytree_return_value(self):
1596 # copytree returns its destination path.
1597 src_dir = self.mkdtemp()
1598 dst_dir = src_dir + "dest"
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001599 self.addCleanup(shutil.rmtree, dst_dir, True)
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001600 src = os.path.join(src_dir, 'foo')
1601 write_file(src, 'foo')
1602 rv = shutil.copytree(src_dir, dst_dir)
1603 self.assertEqual(['foo'], os.listdir(rv))
1604
Christian Heimes9bd667a2008-01-20 15:14:11 +00001605
Brian Curtinc57a3452012-06-22 16:00:30 -05001606class TestWhich(unittest.TestCase):
1607
1608 def setUp(self):
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001609 self.temp_dir = tempfile.mkdtemp(prefix="Tmp")
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001610 self.addCleanup(shutil.rmtree, self.temp_dir, True)
Brian Curtinc57a3452012-06-22 16:00:30 -05001611 # Give the temp_file an ".exe" suffix for all.
1612 # It's needed on Windows and not harmful on other platforms.
1613 self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001614 prefix="Tmp",
1615 suffix=".Exe")
Brian Curtinc57a3452012-06-22 16:00:30 -05001616 os.chmod(self.temp_file.name, stat.S_IXUSR)
1617 self.addCleanup(self.temp_file.close)
1618 self.dir, self.file = os.path.split(self.temp_file.name)
Cheryl Sabella5680f652019-02-13 06:25:10 -05001619 self.env_path = self.dir
1620 self.curdir = os.curdir
1621 self.ext = ".EXE"
Brian Curtinc57a3452012-06-22 16:00:30 -05001622
1623 def test_basic(self):
1624 # Given an EXE in a directory, it should be returned.
1625 rv = shutil.which(self.file, path=self.dir)
1626 self.assertEqual(rv, self.temp_file.name)
1627
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001628 def test_absolute_cmd(self):
Brian Curtinc57a3452012-06-22 16:00:30 -05001629 # When given the fully qualified path to an executable that exists,
1630 # it should be returned.
1631 rv = shutil.which(self.temp_file.name, path=self.temp_dir)
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001632 self.assertEqual(rv, self.temp_file.name)
1633
1634 def test_relative_cmd(self):
1635 # When given the relative path with a directory part to an executable
1636 # that exists, it should be returned.
1637 base_dir, tail_dir = os.path.split(self.dir)
1638 relpath = os.path.join(tail_dir, self.file)
Nick Coghlan55175962013-07-28 22:11:50 +10001639 with support.change_cwd(path=base_dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001640 rv = shutil.which(relpath, path=self.temp_dir)
1641 self.assertEqual(rv, relpath)
1642 # But it shouldn't be searched in PATH directories (issue #16957).
Nick Coghlan55175962013-07-28 22:11:50 +10001643 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001644 rv = shutil.which(relpath, path=base_dir)
1645 self.assertIsNone(rv)
1646
1647 def test_cwd(self):
1648 # Issue #16957
1649 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001650 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001651 rv = shutil.which(self.file, path=base_dir)
1652 if sys.platform == "win32":
1653 # Windows: current directory implicitly on PATH
Cheryl Sabella5680f652019-02-13 06:25:10 -05001654 self.assertEqual(rv, os.path.join(self.curdir, self.file))
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001655 else:
1656 # Other platforms: shouldn't match in the current directory.
1657 self.assertIsNone(rv)
Brian Curtinc57a3452012-06-22 16:00:30 -05001658
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001659 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
1660 'non-root user required')
Brian Curtinc57a3452012-06-22 16:00:30 -05001661 def test_non_matching_mode(self):
1662 # Set the file read-only and ask for writeable files.
1663 os.chmod(self.temp_file.name, stat.S_IREAD)
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001664 if os.access(self.temp_file.name, os.W_OK):
1665 self.skipTest("can't set the file read-only")
Brian Curtinc57a3452012-06-22 16:00:30 -05001666 rv = shutil.which(self.file, path=self.dir, mode=os.W_OK)
1667 self.assertIsNone(rv)
1668
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001669 def test_relative_path(self):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001670 base_dir, tail_dir = os.path.split(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001671 with support.change_cwd(path=base_dir):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001672 rv = shutil.which(self.file, path=tail_dir)
1673 self.assertEqual(rv, os.path.join(tail_dir, self.file))
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001674
Brian Curtinc57a3452012-06-22 16:00:30 -05001675 def test_nonexistent_file(self):
1676 # Return None when no matching executable file is found on the path.
1677 rv = shutil.which("foo.exe", path=self.dir)
1678 self.assertIsNone(rv)
1679
1680 @unittest.skipUnless(sys.platform == "win32",
1681 "pathext check is Windows-only")
1682 def test_pathext_checking(self):
1683 # Ask for the file without the ".exe" extension, then ensure that
1684 # it gets found properly with the extension.
Serhiy Storchakad70127a2013-01-24 20:03:49 +02001685 rv = shutil.which(self.file[:-4], path=self.dir)
Cheryl Sabella5680f652019-02-13 06:25:10 -05001686 self.assertEqual(rv, self.temp_file.name[:-4] + self.ext)
Brian Curtinc57a3452012-06-22 16:00:30 -05001687
Barry Warsaw618738b2013-04-16 11:05:03 -04001688 def test_environ_path(self):
1689 with support.EnvironmentVarGuard() as env:
Cheryl Sabella5680f652019-02-13 06:25:10 -05001690 env['PATH'] = self.env_path
Barry Warsaw618738b2013-04-16 11:05:03 -04001691 rv = shutil.which(self.file)
1692 self.assertEqual(rv, self.temp_file.name)
1693
Victor Stinner228a3c92019-04-17 16:26:36 +02001694 def test_environ_path_empty(self):
1695 # PATH='': no match
1696 with support.EnvironmentVarGuard() as env:
1697 env['PATH'] = ''
1698 with unittest.mock.patch('os.confstr', return_value=self.dir, \
1699 create=True), \
1700 support.swap_attr(os, 'defpath', self.dir), \
1701 support.change_cwd(self.dir):
1702 rv = shutil.which(self.file)
1703 self.assertIsNone(rv)
1704
1705 def test_environ_path_cwd(self):
1706 expected_cwd = os.path.basename(self.temp_file.name)
1707 if sys.platform == "win32":
1708 curdir = os.curdir
1709 if isinstance(expected_cwd, bytes):
1710 curdir = os.fsencode(curdir)
1711 expected_cwd = os.path.join(curdir, expected_cwd)
1712
1713 # PATH=':': explicitly looks in the current directory
1714 with support.EnvironmentVarGuard() as env:
1715 env['PATH'] = os.pathsep
1716 with unittest.mock.patch('os.confstr', return_value=self.dir, \
1717 create=True), \
1718 support.swap_attr(os, 'defpath', self.dir):
1719 rv = shutil.which(self.file)
1720 self.assertIsNone(rv)
1721
1722 # look in current directory
1723 with support.change_cwd(self.dir):
1724 rv = shutil.which(self.file)
1725 self.assertEqual(rv, expected_cwd)
1726
1727 def test_environ_path_missing(self):
1728 with support.EnvironmentVarGuard() as env:
1729 env.pop('PATH', None)
1730
1731 # without confstr
1732 with unittest.mock.patch('os.confstr', side_effect=ValueError, \
1733 create=True), \
1734 support.swap_attr(os, 'defpath', self.dir):
1735 rv = shutil.which(self.file)
1736 self.assertEqual(rv, self.temp_file.name)
1737
1738 # with confstr
1739 with unittest.mock.patch('os.confstr', return_value=self.dir, \
1740 create=True), \
1741 support.swap_attr(os, 'defpath', ''):
1742 rv = shutil.which(self.file)
1743 self.assertEqual(rv, self.temp_file.name)
1744
Barry Warsaw618738b2013-04-16 11:05:03 -04001745 def test_empty_path(self):
1746 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001747 with support.change_cwd(path=self.dir), \
Barry Warsaw618738b2013-04-16 11:05:03 -04001748 support.EnvironmentVarGuard() as env:
Cheryl Sabella5680f652019-02-13 06:25:10 -05001749 env['PATH'] = self.env_path
Barry Warsaw618738b2013-04-16 11:05:03 -04001750 rv = shutil.which(self.file, path='')
1751 self.assertIsNone(rv)
1752
1753 def test_empty_path_no_PATH(self):
1754 with support.EnvironmentVarGuard() as env:
1755 env.pop('PATH', None)
1756 rv = shutil.which(self.file)
1757 self.assertIsNone(rv)
1758
Victor Stinner228a3c92019-04-17 16:26:36 +02001759 @unittest.skipUnless(sys.platform == "win32", 'test specific to Windows')
1760 def test_pathext(self):
1761 ext = ".xyz"
1762 temp_filexyz = tempfile.NamedTemporaryFile(dir=self.temp_dir,
1763 prefix="Tmp2", suffix=ext)
1764 os.chmod(temp_filexyz.name, stat.S_IXUSR)
1765 self.addCleanup(temp_filexyz.close)
1766
1767 # strip path and extension
1768 program = os.path.basename(temp_filexyz.name)
1769 program = os.path.splitext(program)[0]
1770
1771 with support.EnvironmentVarGuard() as env:
1772 env['PATHEXT'] = ext
1773 rv = shutil.which(program, path=self.temp_dir)
1774 self.assertEqual(rv, temp_filexyz.name)
1775
Brian Curtinc57a3452012-06-22 16:00:30 -05001776
Cheryl Sabella5680f652019-02-13 06:25:10 -05001777class TestWhichBytes(TestWhich):
1778 def setUp(self):
1779 TestWhich.setUp(self)
1780 self.dir = os.fsencode(self.dir)
1781 self.file = os.fsencode(self.file)
1782 self.temp_file.name = os.fsencode(self.temp_file.name)
1783 self.curdir = os.fsencode(self.curdir)
1784 self.ext = os.fsencode(self.ext)
1785
1786
Christian Heimesada8c3b2008-03-18 18:26:33 +00001787class TestMove(unittest.TestCase):
1788
1789 def setUp(self):
1790 filename = "foo"
1791 self.src_dir = tempfile.mkdtemp()
1792 self.dst_dir = tempfile.mkdtemp()
1793 self.src_file = os.path.join(self.src_dir, filename)
1794 self.dst_file = os.path.join(self.dst_dir, filename)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001795 with open(self.src_file, "wb") as f:
1796 f.write(b"spam")
1797
1798 def tearDown(self):
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001799 for d in (self.src_dir, self.dst_dir):
Christian Heimesada8c3b2008-03-18 18:26:33 +00001800 try:
1801 if d:
1802 shutil.rmtree(d)
1803 except:
1804 pass
1805
1806 def _check_move_file(self, src, dst, real_dst):
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001807 with open(src, "rb") as f:
1808 contents = f.read()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001809 shutil.move(src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001810 with open(real_dst, "rb") as f:
1811 self.assertEqual(contents, f.read())
Christian Heimesada8c3b2008-03-18 18:26:33 +00001812 self.assertFalse(os.path.exists(src))
1813
1814 def _check_move_dir(self, src, dst, real_dst):
1815 contents = sorted(os.listdir(src))
1816 shutil.move(src, dst)
1817 self.assertEqual(contents, sorted(os.listdir(real_dst)))
1818 self.assertFalse(os.path.exists(src))
1819
1820 def test_move_file(self):
1821 # Move a file to another location on the same filesystem.
1822 self._check_move_file(self.src_file, self.dst_file, self.dst_file)
1823
1824 def test_move_file_to_dir(self):
1825 # Move a file inside an existing dir on the same filesystem.
1826 self._check_move_file(self.src_file, self.dst_dir, self.dst_file)
1827
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001828 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001829 def test_move_file_other_fs(self):
1830 # Move a file to an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001831 self.test_move_file()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001832
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001833 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001834 def test_move_file_to_dir_other_fs(self):
1835 # Move a file to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001836 self.test_move_file_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001837
1838 def test_move_dir(self):
1839 # Move a dir to another location on the same filesystem.
1840 dst_dir = tempfile.mktemp()
1841 try:
1842 self._check_move_dir(self.src_dir, dst_dir, dst_dir)
1843 finally:
1844 try:
1845 shutil.rmtree(dst_dir)
1846 except:
1847 pass
1848
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001849 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001850 def test_move_dir_other_fs(self):
1851 # Move a dir to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001852 self.test_move_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001853
1854 def test_move_dir_to_dir(self):
1855 # Move a dir inside an existing dir on the same filesystem.
1856 self._check_move_dir(self.src_dir, self.dst_dir,
1857 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1858
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001859 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001860 def test_move_dir_to_dir_other_fs(self):
1861 # Move a dir inside an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001862 self.test_move_dir_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001863
Serhiy Storchaka3a308b92014-02-11 10:30:59 +02001864 def test_move_dir_sep_to_dir(self):
1865 self._check_move_dir(self.src_dir + os.path.sep, self.dst_dir,
1866 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1867
1868 @unittest.skipUnless(os.path.altsep, 'requires os.path.altsep')
1869 def test_move_dir_altsep_to_dir(self):
1870 self._check_move_dir(self.src_dir + os.path.altsep, self.dst_dir,
1871 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1872
Christian Heimesada8c3b2008-03-18 18:26:33 +00001873 def test_existing_file_inside_dest_dir(self):
1874 # A file with the same name inside the destination dir already exists.
1875 with open(self.dst_file, "wb"):
1876 pass
1877 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir)
1878
1879 def test_dont_move_dir_in_itself(self):
1880 # Moving a dir inside itself raises an Error.
1881 dst = os.path.join(self.src_dir, "bar")
1882 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst)
1883
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001884 def test_destinsrc_false_negative(self):
1885 os.mkdir(TESTFN)
1886 try:
1887 for src, dst in [('srcdir', 'srcdir/dest')]:
1888 src = os.path.join(TESTFN, src)
1889 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001890 self.assertTrue(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001891 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001892 'dst (%s) is not in src (%s)' % (dst, src))
1893 finally:
1894 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001895
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001896 def test_destinsrc_false_positive(self):
1897 os.mkdir(TESTFN)
1898 try:
1899 for src, dst in [('srcdir', 'src/dest'), ('srcdir', 'srcdir.new')]:
1900 src = os.path.join(TESTFN, src)
1901 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001902 self.assertFalse(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001903 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001904 'dst (%s) is in src (%s)' % (dst, src))
1905 finally:
1906 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimes9bd667a2008-01-20 15:14:11 +00001907
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001908 @support.skip_unless_symlink
1909 @mock_rename
1910 def test_move_file_symlink(self):
1911 dst = os.path.join(self.src_dir, 'bar')
1912 os.symlink(self.src_file, dst)
1913 shutil.move(dst, self.dst_file)
1914 self.assertTrue(os.path.islink(self.dst_file))
1915 self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
1916
1917 @support.skip_unless_symlink
1918 @mock_rename
1919 def test_move_file_symlink_to_dir(self):
1920 filename = "bar"
1921 dst = os.path.join(self.src_dir, filename)
1922 os.symlink(self.src_file, dst)
1923 shutil.move(dst, self.dst_dir)
1924 final_link = os.path.join(self.dst_dir, filename)
1925 self.assertTrue(os.path.islink(final_link))
1926 self.assertTrue(os.path.samefile(self.src_file, final_link))
1927
1928 @support.skip_unless_symlink
1929 @mock_rename
1930 def test_move_dangling_symlink(self):
1931 src = os.path.join(self.src_dir, 'baz')
1932 dst = os.path.join(self.src_dir, 'bar')
1933 os.symlink(src, dst)
1934 dst_link = os.path.join(self.dst_dir, 'quux')
1935 shutil.move(dst, dst_link)
1936 self.assertTrue(os.path.islink(dst_link))
Miss Islington (bot)c30c8692019-08-21 14:09:33 -07001937 self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001938
1939 @support.skip_unless_symlink
1940 @mock_rename
1941 def test_move_dir_symlink(self):
1942 src = os.path.join(self.src_dir, 'baz')
1943 dst = os.path.join(self.src_dir, 'bar')
1944 os.mkdir(src)
1945 os.symlink(src, dst)
1946 dst_link = os.path.join(self.dst_dir, 'quux')
1947 shutil.move(dst, dst_link)
1948 self.assertTrue(os.path.islink(dst_link))
1949 self.assertTrue(os.path.samefile(src, dst_link))
1950
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001951 def test_move_return_value(self):
1952 rv = shutil.move(self.src_file, self.dst_dir)
1953 self.assertEqual(rv,
1954 os.path.join(self.dst_dir, os.path.basename(self.src_file)))
1955
1956 def test_move_as_rename_return_value(self):
1957 rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar'))
1958 self.assertEqual(rv, os.path.join(self.dst_dir, 'bar'))
1959
R David Murray6ffface2014-06-11 14:40:13 -04001960 @mock_rename
1961 def test_move_file_special_function(self):
1962 moved = []
1963 def _copy(src, dst):
1964 moved.append((src, dst))
1965 shutil.move(self.src_file, self.dst_dir, copy_function=_copy)
1966 self.assertEqual(len(moved), 1)
1967
1968 @mock_rename
1969 def test_move_dir_special_function(self):
1970 moved = []
1971 def _copy(src, dst):
1972 moved.append((src, dst))
1973 support.create_empty_file(os.path.join(self.src_dir, 'child'))
1974 support.create_empty_file(os.path.join(self.src_dir, 'child1'))
1975 shutil.move(self.src_dir, self.dst_dir, copy_function=_copy)
1976 self.assertEqual(len(moved), 3)
1977
Tarek Ziadé5340db32010-04-19 22:30:51 +00001978
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001979class TestCopyFile(unittest.TestCase):
1980
1981 _delete = False
1982
1983 class Faux(object):
1984 _entered = False
1985 _exited_with = None
1986 _raised = False
1987 def __init__(self, raise_in_exit=False, suppress_at_exit=True):
1988 self._raise_in_exit = raise_in_exit
1989 self._suppress_at_exit = suppress_at_exit
1990 def read(self, *args):
1991 return ''
1992 def __enter__(self):
1993 self._entered = True
1994 def __exit__(self, exc_type, exc_val, exc_tb):
1995 self._exited_with = exc_type, exc_val, exc_tb
1996 if self._raise_in_exit:
1997 self._raised = True
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001998 raise OSError("Cannot close")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001999 return self._suppress_at_exit
2000
2001 def tearDown(self):
2002 if self._delete:
2003 del shutil.open
2004
2005 def _set_shutil_open(self, func):
2006 shutil.open = func
2007 self._delete = True
2008
2009 def test_w_source_open_fails(self):
2010 def _open(filename, mode='r'):
2011 if filename == 'srcfile':
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002012 raise OSError('Cannot open "srcfile"')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002013 assert 0 # shouldn't reach here.
2014
2015 self._set_shutil_open(_open)
2016
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002017 self.assertRaises(OSError, shutil.copyfile, 'srcfile', 'destfile')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002018
Victor Stinner937ee9e2018-06-26 02:11:06 +02002019 @unittest.skipIf(MACOS, "skipped on macOS")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002020 def test_w_dest_open_fails(self):
2021
2022 srcfile = self.Faux()
2023
2024 def _open(filename, mode='r'):
2025 if filename == 'srcfile':
2026 return srcfile
2027 if filename == 'destfile':
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002028 raise OSError('Cannot open "destfile"')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002029 assert 0 # shouldn't reach here.
2030
2031 self._set_shutil_open(_open)
2032
2033 shutil.copyfile('srcfile', 'destfile')
2034 self.assertTrue(srcfile._entered)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002035 self.assertTrue(srcfile._exited_with[0] is OSError)
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002036 self.assertEqual(srcfile._exited_with[1].args,
2037 ('Cannot open "destfile"',))
2038
Victor Stinner937ee9e2018-06-26 02:11:06 +02002039 @unittest.skipIf(MACOS, "skipped on macOS")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002040 def test_w_dest_close_fails(self):
2041
2042 srcfile = self.Faux()
2043 destfile = self.Faux(True)
2044
2045 def _open(filename, mode='r'):
2046 if filename == 'srcfile':
2047 return srcfile
2048 if filename == 'destfile':
2049 return destfile
2050 assert 0 # shouldn't reach here.
2051
2052 self._set_shutil_open(_open)
2053
2054 shutil.copyfile('srcfile', 'destfile')
2055 self.assertTrue(srcfile._entered)
2056 self.assertTrue(destfile._entered)
2057 self.assertTrue(destfile._raised)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002058 self.assertTrue(srcfile._exited_with[0] is OSError)
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002059 self.assertEqual(srcfile._exited_with[1].args,
2060 ('Cannot close',))
2061
Victor Stinner937ee9e2018-06-26 02:11:06 +02002062 @unittest.skipIf(MACOS, "skipped on macOS")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002063 def test_w_source_close_fails(self):
2064
2065 srcfile = self.Faux(True)
2066 destfile = self.Faux()
2067
2068 def _open(filename, mode='r'):
2069 if filename == 'srcfile':
2070 return srcfile
2071 if filename == 'destfile':
2072 return destfile
2073 assert 0 # shouldn't reach here.
2074
2075 self._set_shutil_open(_open)
2076
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002077 self.assertRaises(OSError,
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002078 shutil.copyfile, 'srcfile', 'destfile')
2079 self.assertTrue(srcfile._entered)
2080 self.assertTrue(destfile._entered)
2081 self.assertFalse(destfile._raised)
2082 self.assertTrue(srcfile._exited_with[0] is None)
2083 self.assertTrue(srcfile._raised)
2084
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002085 def test_move_dir_caseinsensitive(self):
2086 # Renames a folder to the same name
2087 # but a different case.
2088
2089 self.src_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07002090 self.addCleanup(shutil.rmtree, self.src_dir, True)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002091 dst_dir = os.path.join(
2092 os.path.dirname(self.src_dir),
2093 os.path.basename(self.src_dir).upper())
2094 self.assertNotEqual(self.src_dir, dst_dir)
2095
2096 try:
2097 shutil.move(self.src_dir, dst_dir)
2098 self.assertTrue(os.path.isdir(dst_dir))
2099 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +02002100 os.rmdir(dst_dir)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002101
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002102
Giampaolo Rodolac7f02a92018-06-19 08:27:29 -07002103class TestCopyFileObj(unittest.TestCase):
2104 FILESIZE = 2 * 1024 * 1024
2105
2106 @classmethod
2107 def setUpClass(cls):
2108 write_test_file(TESTFN, cls.FILESIZE)
2109
2110 @classmethod
2111 def tearDownClass(cls):
2112 support.unlink(TESTFN)
2113 support.unlink(TESTFN2)
2114
2115 def tearDown(self):
2116 support.unlink(TESTFN2)
2117
2118 @contextlib.contextmanager
2119 def get_files(self):
2120 with open(TESTFN, "rb") as src:
2121 with open(TESTFN2, "wb") as dst:
2122 yield (src, dst)
2123
2124 def assert_files_eq(self, src, dst):
2125 with open(src, 'rb') as fsrc:
2126 with open(dst, 'rb') as fdst:
2127 self.assertEqual(fsrc.read(), fdst.read())
2128
2129 def test_content(self):
2130 with self.get_files() as (src, dst):
2131 shutil.copyfileobj(src, dst)
2132 self.assert_files_eq(TESTFN, TESTFN2)
2133
2134 def test_file_not_closed(self):
2135 with self.get_files() as (src, dst):
2136 shutil.copyfileobj(src, dst)
2137 assert not src.closed
2138 assert not dst.closed
2139
2140 def test_file_offset(self):
2141 with self.get_files() as (src, dst):
2142 shutil.copyfileobj(src, dst)
2143 self.assertEqual(src.tell(), self.FILESIZE)
2144 self.assertEqual(dst.tell(), self.FILESIZE)
2145
2146 @unittest.skipIf(os.name != 'nt', "Windows only")
2147 def test_win_impl(self):
2148 # Make sure alternate Windows implementation is called.
2149 with unittest.mock.patch("shutil._copyfileobj_readinto") as m:
2150 shutil.copyfile(TESTFN, TESTFN2)
2151 assert m.called
2152
2153 # File size is 2 MiB but max buf size should be 1 MiB.
2154 self.assertEqual(m.call_args[0][2], 1 * 1024 * 1024)
2155
2156 # If file size < 1 MiB memoryview() length must be equal to
2157 # the actual file size.
2158 with tempfile.NamedTemporaryFile(delete=False) as f:
2159 f.write(b'foo')
2160 fname = f.name
2161 self.addCleanup(support.unlink, fname)
2162 with unittest.mock.patch("shutil._copyfileobj_readinto") as m:
2163 shutil.copyfile(fname, TESTFN2)
2164 self.assertEqual(m.call_args[0][2], 3)
2165
2166 # Empty files should not rely on readinto() variant.
2167 with tempfile.NamedTemporaryFile(delete=False) as f:
2168 pass
2169 fname = f.name
2170 self.addCleanup(support.unlink, fname)
2171 with unittest.mock.patch("shutil._copyfileobj_readinto") as m:
2172 shutil.copyfile(fname, TESTFN2)
2173 assert not m.called
2174 self.assert_files_eq(fname, TESTFN2)
2175
2176
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002177class _ZeroCopyFileTest(object):
2178 """Tests common to all zero-copy APIs."""
2179 FILESIZE = (10 * 1024 * 1024) # 10 MiB
2180 FILEDATA = b""
2181 PATCHPOINT = ""
2182
2183 @classmethod
2184 def setUpClass(cls):
2185 write_test_file(TESTFN, cls.FILESIZE)
2186 with open(TESTFN, 'rb') as f:
2187 cls.FILEDATA = f.read()
2188 assert len(cls.FILEDATA) == cls.FILESIZE
2189
2190 @classmethod
2191 def tearDownClass(cls):
2192 support.unlink(TESTFN)
2193
2194 def tearDown(self):
2195 support.unlink(TESTFN2)
2196
2197 @contextlib.contextmanager
2198 def get_files(self):
2199 with open(TESTFN, "rb") as src:
2200 with open(TESTFN2, "wb") as dst:
2201 yield (src, dst)
2202
2203 def zerocopy_fun(self, *args, **kwargs):
2204 raise NotImplementedError("must be implemented in subclass")
2205
2206 def reset(self):
2207 self.tearDown()
2208 self.tearDownClass()
2209 self.setUpClass()
2210 self.setUp()
2211
2212 # ---
2213
2214 def test_regular_copy(self):
2215 with self.get_files() as (src, dst):
2216 self.zerocopy_fun(src, dst)
2217 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2218 # Make sure the fallback function is not called.
2219 with self.get_files() as (src, dst):
2220 with unittest.mock.patch('shutil.copyfileobj') as m:
2221 shutil.copyfile(TESTFN, TESTFN2)
2222 assert not m.called
2223
2224 def test_same_file(self):
2225 self.addCleanup(self.reset)
2226 with self.get_files() as (src, dst):
2227 with self.assertRaises(Exception):
2228 self.zerocopy_fun(src, src)
2229 # Make sure src file is not corrupted.
2230 self.assertEqual(read_file(TESTFN, binary=True), self.FILEDATA)
2231
2232 def test_non_existent_src(self):
2233 name = tempfile.mktemp()
2234 with self.assertRaises(FileNotFoundError) as cm:
2235 shutil.copyfile(name, "new")
2236 self.assertEqual(cm.exception.filename, name)
2237
2238 def test_empty_file(self):
2239 srcname = TESTFN + 'src'
2240 dstname = TESTFN + 'dst'
2241 self.addCleanup(lambda: support.unlink(srcname))
2242 self.addCleanup(lambda: support.unlink(dstname))
2243 with open(srcname, "wb"):
2244 pass
2245
2246 with open(srcname, "rb") as src:
2247 with open(dstname, "wb") as dst:
2248 self.zerocopy_fun(src, dst)
2249
2250 self.assertEqual(read_file(dstname, binary=True), b"")
2251
2252 def test_unhandled_exception(self):
2253 with unittest.mock.patch(self.PATCHPOINT,
2254 side_effect=ZeroDivisionError):
2255 self.assertRaises(ZeroDivisionError,
2256 shutil.copyfile, TESTFN, TESTFN2)
2257
2258 def test_exception_on_first_call(self):
2259 # Emulate a case where the first call to the zero-copy
2260 # function raises an exception in which case the function is
2261 # supposed to give up immediately.
2262 with unittest.mock.patch(self.PATCHPOINT,
2263 side_effect=OSError(errno.EINVAL, "yo")):
2264 with self.get_files() as (src, dst):
2265 with self.assertRaises(_GiveupOnFastCopy):
2266 self.zerocopy_fun(src, dst)
2267
2268 def test_filesystem_full(self):
2269 # Emulate a case where filesystem is full and sendfile() fails
2270 # on first call.
2271 with unittest.mock.patch(self.PATCHPOINT,
2272 side_effect=OSError(errno.ENOSPC, "yo")):
2273 with self.get_files() as (src, dst):
2274 self.assertRaises(OSError, self.zerocopy_fun, src, dst)
2275
2276
2277@unittest.skipIf(not SUPPORTS_SENDFILE, 'os.sendfile() not supported')
2278class TestZeroCopySendfile(_ZeroCopyFileTest, unittest.TestCase):
2279 PATCHPOINT = "os.sendfile"
2280
2281 def zerocopy_fun(self, fsrc, fdst):
2282 return shutil._fastcopy_sendfile(fsrc, fdst)
2283
2284 def test_non_regular_file_src(self):
2285 with io.BytesIO(self.FILEDATA) as src:
2286 with open(TESTFN2, "wb") as dst:
2287 with self.assertRaises(_GiveupOnFastCopy):
2288 self.zerocopy_fun(src, dst)
2289 shutil.copyfileobj(src, dst)
2290
2291 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2292
2293 def test_non_regular_file_dst(self):
2294 with open(TESTFN, "rb") as src:
2295 with io.BytesIO() as dst:
2296 with self.assertRaises(_GiveupOnFastCopy):
2297 self.zerocopy_fun(src, dst)
2298 shutil.copyfileobj(src, dst)
2299 dst.seek(0)
2300 self.assertEqual(dst.read(), self.FILEDATA)
2301
2302 def test_exception_on_second_call(self):
2303 def sendfile(*args, **kwargs):
2304 if not flag:
2305 flag.append(None)
2306 return orig_sendfile(*args, **kwargs)
2307 else:
2308 raise OSError(errno.EBADF, "yo")
2309
2310 flag = []
2311 orig_sendfile = os.sendfile
2312 with unittest.mock.patch('os.sendfile', create=True,
2313 side_effect=sendfile):
2314 with self.get_files() as (src, dst):
2315 with self.assertRaises(OSError) as cm:
2316 shutil._fastcopy_sendfile(src, dst)
2317 assert flag
2318 self.assertEqual(cm.exception.errno, errno.EBADF)
2319
2320 def test_cant_get_size(self):
2321 # Emulate a case where src file size cannot be determined.
2322 # Internally bufsize will be set to a small value and
2323 # sendfile() will be called repeatedly.
2324 with unittest.mock.patch('os.fstat', side_effect=OSError) as m:
2325 with self.get_files() as (src, dst):
2326 shutil._fastcopy_sendfile(src, dst)
2327 assert m.called
2328 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2329
2330 def test_small_chunks(self):
2331 # Force internal file size detection to be smaller than the
2332 # actual file size. We want to force sendfile() to be called
2333 # multiple times, also in order to emulate a src fd which gets
2334 # bigger while it is being copied.
2335 mock = unittest.mock.Mock()
2336 mock.st_size = 65536 + 1
2337 with unittest.mock.patch('os.fstat', return_value=mock) as m:
2338 with self.get_files() as (src, dst):
2339 shutil._fastcopy_sendfile(src, dst)
2340 assert m.called
2341 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2342
2343 def test_big_chunk(self):
2344 # Force internal file size detection to be +100MB bigger than
2345 # the actual file size. Make sure sendfile() does not rely on
2346 # file size value except for (maybe) a better throughput /
2347 # performance.
2348 mock = unittest.mock.Mock()
2349 mock.st_size = self.FILESIZE + (100 * 1024 * 1024)
2350 with unittest.mock.patch('os.fstat', return_value=mock) as m:
2351 with self.get_files() as (src, dst):
2352 shutil._fastcopy_sendfile(src, dst)
2353 assert m.called
2354 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2355
2356 def test_blocksize_arg(self):
2357 with unittest.mock.patch('os.sendfile',
2358 side_effect=ZeroDivisionError) as m:
2359 self.assertRaises(ZeroDivisionError,
2360 shutil.copyfile, TESTFN, TESTFN2)
2361 blocksize = m.call_args[0][3]
2362 # Make sure file size and the block size arg passed to
2363 # sendfile() are the same.
2364 self.assertEqual(blocksize, os.path.getsize(TESTFN))
2365 # ...unless we're dealing with a small file.
2366 support.unlink(TESTFN2)
2367 write_file(TESTFN2, b"hello", binary=True)
2368 self.addCleanup(support.unlink, TESTFN2 + '3')
2369 self.assertRaises(ZeroDivisionError,
2370 shutil.copyfile, TESTFN2, TESTFN2 + '3')
2371 blocksize = m.call_args[0][3]
2372 self.assertEqual(blocksize, 2 ** 23)
2373
2374 def test_file2file_not_supported(self):
2375 # Emulate a case where sendfile() only support file->socket
2376 # fds. In such a case copyfile() is supposed to skip the
2377 # fast-copy attempt from then on.
Giampaolo Rodola413d9552019-05-30 14:05:41 +08002378 assert shutil._USE_CP_SENDFILE
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002379 try:
2380 with unittest.mock.patch(
2381 self.PATCHPOINT,
2382 side_effect=OSError(errno.ENOTSOCK, "yo")) as m:
2383 with self.get_files() as (src, dst):
2384 with self.assertRaises(_GiveupOnFastCopy):
2385 shutil._fastcopy_sendfile(src, dst)
2386 assert m.called
Giampaolo Rodola413d9552019-05-30 14:05:41 +08002387 assert not shutil._USE_CP_SENDFILE
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002388
2389 with unittest.mock.patch(self.PATCHPOINT) as m:
2390 shutil.copyfile(TESTFN, TESTFN2)
2391 assert not m.called
2392 finally:
Giampaolo Rodola413d9552019-05-30 14:05:41 +08002393 shutil._USE_CP_SENDFILE = True
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002394
2395
Victor Stinner937ee9e2018-06-26 02:11:06 +02002396@unittest.skipIf(not MACOS, 'macOS only')
Giampaolo Rodolac7f02a92018-06-19 08:27:29 -07002397class TestZeroCopyMACOS(_ZeroCopyFileTest, unittest.TestCase):
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002398 PATCHPOINT = "posix._fcopyfile"
2399
2400 def zerocopy_fun(self, src, dst):
Giampaolo Rodolac7f02a92018-06-19 08:27:29 -07002401 return shutil._fastcopy_fcopyfile(src, dst, posix._COPYFILE_DATA)
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002402
2403
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002404class TermsizeTests(unittest.TestCase):
2405 def test_does_not_crash(self):
2406 """Check if get_terminal_size() returns a meaningful value.
2407
2408 There's no easy portable way to actually check the size of the
2409 terminal, so let's check if it returns something sensible instead.
2410 """
2411 size = shutil.get_terminal_size()
Antoine Pitroucfade362012-02-08 23:48:59 +01002412 self.assertGreaterEqual(size.columns, 0)
2413 self.assertGreaterEqual(size.lines, 0)
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002414
2415 def test_os_environ_first(self):
2416 "Check if environment variables have precedence"
2417
2418 with support.EnvironmentVarGuard() as env:
2419 env['COLUMNS'] = '777'
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002420 del env['LINES']
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002421 size = shutil.get_terminal_size()
2422 self.assertEqual(size.columns, 777)
2423
2424 with support.EnvironmentVarGuard() as env:
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002425 del env['COLUMNS']
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002426 env['LINES'] = '888'
2427 size = shutil.get_terminal_size()
2428 self.assertEqual(size.lines, 888)
2429
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002430 def test_bad_environ(self):
2431 with support.EnvironmentVarGuard() as env:
2432 env['COLUMNS'] = 'xxx'
2433 env['LINES'] = 'yyy'
2434 size = shutil.get_terminal_size()
2435 self.assertGreaterEqual(size.columns, 0)
2436 self.assertGreaterEqual(size.lines, 0)
2437
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002438 @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
Victor Stinner119ebb72016-04-19 22:24:56 +02002439 @unittest.skipUnless(hasattr(os, 'get_terminal_size'),
2440 'need os.get_terminal_size()')
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002441 def test_stty_match(self):
2442 """Check if stty returns the same results ignoring env
2443
2444 This test will fail if stdin and stdout are connected to
2445 different terminals with different sizes. Nevertheless, such
2446 situations should be pretty rare.
2447 """
2448 try:
2449 size = subprocess.check_output(['stty', 'size']).decode().split()
Xavier de Gaye38c8b7d2016-11-14 17:14:42 +01002450 except (FileNotFoundError, PermissionError,
2451 subprocess.CalledProcessError):
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002452 self.skipTest("stty invocation failed")
2453 expected = (int(size[1]), int(size[0])) # reversed order
2454
2455 with support.EnvironmentVarGuard() as env:
2456 del env['LINES']
2457 del env['COLUMNS']
2458 actual = shutil.get_terminal_size()
2459
2460 self.assertEqual(expected, actual)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002461
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002462 def test_fallback(self):
2463 with support.EnvironmentVarGuard() as env:
2464 del env['LINES']
2465 del env['COLUMNS']
2466
2467 # sys.__stdout__ has no fileno()
2468 with support.swap_attr(sys, '__stdout__', None):
2469 size = shutil.get_terminal_size(fallback=(10, 20))
2470 self.assertEqual(size.columns, 10)
2471 self.assertEqual(size.lines, 20)
2472
2473 # sys.__stdout__ is not a terminal on Unix
2474 # or fileno() not in (0, 1, 2) on Windows
2475 with open(os.devnull, 'w') as f, \
2476 support.swap_attr(sys, '__stdout__', f):
2477 size = shutil.get_terminal_size(fallback=(30, 40))
2478 self.assertEqual(size.columns, 30)
2479 self.assertEqual(size.lines, 40)
2480
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002481
Berker Peksag8083cd62014-11-01 11:04:06 +02002482class PublicAPITests(unittest.TestCase):
2483 """Ensures that the correct values are exposed in the public API."""
2484
2485 def test_module_all_attribute(self):
2486 self.assertTrue(hasattr(shutil, '__all__'))
2487 target_api = ['copyfileobj', 'copyfile', 'copymode', 'copystat',
2488 'copy', 'copy2', 'copytree', 'move', 'rmtree', 'Error',
2489 'SpecialFileError', 'ExecError', 'make_archive',
2490 'get_archive_formats', 'register_archive_format',
2491 'unregister_archive_format', 'get_unpack_formats',
2492 'register_unpack_format', 'unregister_unpack_format',
2493 'unpack_archive', 'ignore_patterns', 'chown', 'which',
2494 'get_terminal_size', 'SameFileError']
2495 if hasattr(os, 'statvfs') or os.name == 'nt':
2496 target_api.append('disk_usage')
2497 self.assertEqual(set(shutil.__all__), set(target_api))
2498
2499
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00002500if __name__ == '__main__':
Brett Cannon3e9a9ae2013-06-12 21:25:59 -04002501 unittest.main()