blob: 149e4c34f7db03d6066febb21386b0930a4436eb [file] [log] [blame]
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001# Copyright (C) 2003 Python Software Foundation
2
3import unittest
4import shutil
5import tempfile
Johannes Gijsbers8e6f2de2004-11-23 09:27:27 +00006import sys
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +00007import stat
Brett Cannon1c3fa182004-06-19 21:11:35 +00008import os
9import os.path
Antoine Pitrouc041ab62012-01-02 19:18:02 +010010import errno
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040011import functools
Antoine Pitroubcf2b592012-02-08 23:28:36 +010012import subprocess
Benjamin Petersonee8712c2008-05-20 21:35:26 +000013from test import support
14from test.support import TESTFN
Tarek Ziadé396fad72010-02-23 05:30:31 +000015from os.path import splitdrive
16from distutils.spawn import find_executable, spawn
17from shutil import (_make_tarball, _make_zipfile, make_archive,
18 register_archive_format, unregister_archive_format,
Tarek Ziadé6ac91722010-04-28 17:51:36 +000019 get_archive_formats, Error, unpack_archive,
20 register_unpack_format, RegistryError,
Hynek Schlawack26fe37d2012-07-19 21:41:02 +020021 unregister_unpack_format, get_unpack_formats)
Tarek Ziadé396fad72010-02-23 05:30:31 +000022import tarfile
23import warnings
24
25from test import support
Ezio Melotti975077a2011-05-19 22:03:22 +030026from test.support import TESTFN, check_warnings, captured_stdout, requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +000027
Tarek Ziadéffa155a2010-04-29 13:34:35 +000028try:
29 import bz2
30 BZ2_SUPPORTED = True
31except ImportError:
32 BZ2_SUPPORTED = False
33
Antoine Pitrou7fff0962009-05-01 21:09:44 +000034TESTFN2 = TESTFN + "2"
Barry Warsaw7fc2cca2003-01-24 17:34:13 +000035
Tarek Ziadé396fad72010-02-23 05:30:31 +000036try:
37 import grp
38 import pwd
39 UID_GID_SUPPORT = True
40except ImportError:
41 UID_GID_SUPPORT = False
42
43try:
Tarek Ziadé396fad72010-02-23 05:30:31 +000044 import zipfile
45 ZIP_SUPPORT = True
46except ImportError:
47 ZIP_SUPPORT = find_executable('zip')
48
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040049def _fake_rename(*args, **kwargs):
50 # Pretend the destination path is on a different filesystem.
Antoine Pitrouc041ab62012-01-02 19:18:02 +010051 raise OSError(getattr(errno, 'EXDEV', 18), "Invalid cross-device link")
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040052
53def mock_rename(func):
54 @functools.wraps(func)
55 def wrap(*args, **kwargs):
56 try:
57 builtin_rename = os.rename
58 os.rename = _fake_rename
59 return func(*args, **kwargs)
60 finally:
61 os.rename = builtin_rename
62 return wrap
63
Éric Araujoa7e33a12011-08-12 19:51:35 +020064def write_file(path, content, binary=False):
65 """Write *content* to a file located at *path*.
66
67 If *path* is a tuple instead of a string, os.path.join will be used to
68 make a path. If *binary* is true, the file will be opened in binary
69 mode.
70 """
71 if isinstance(path, tuple):
72 path = os.path.join(*path)
73 with open(path, 'wb' if binary else 'w') as fp:
74 fp.write(content)
75
76def read_file(path, binary=False):
77 """Return contents from a file located at *path*.
78
79 If *path* is a tuple instead of a string, os.path.join will be used to
80 make a path. If *binary* is true, the file will be opened in binary
81 mode.
82 """
83 if isinstance(path, tuple):
84 path = os.path.join(*path)
85 with open(path, 'rb' if binary else 'r') as fp:
86 return fp.read()
87
88
Barry Warsaw7fc2cca2003-01-24 17:34:13 +000089class TestShutil(unittest.TestCase):
Tarek Ziadé396fad72010-02-23 05:30:31 +000090
91 def setUp(self):
92 super(TestShutil, self).setUp()
93 self.tempdirs = []
94
95 def tearDown(self):
96 super(TestShutil, self).tearDown()
97 while self.tempdirs:
98 d = self.tempdirs.pop()
99 shutil.rmtree(d, os.name in ('nt', 'cygwin'))
100
Tarek Ziadé396fad72010-02-23 05:30:31 +0000101
102 def mkdtemp(self):
103 """Create a temporary directory that will be cleaned up.
104
105 Returns the path of the directory.
106 """
107 d = tempfile.mkdtemp()
108 self.tempdirs.append(d)
109 return d
Tarek Ziadé5340db32010-04-19 22:30:51 +0000110
Hynek Schlawack3b527782012-06-25 13:27:31 +0200111 def test_rmtree_works_on_bytes(self):
112 tmp = self.mkdtemp()
113 victim = os.path.join(tmp, 'killme')
114 os.mkdir(victim)
115 write_file(os.path.join(victim, 'somefile'), 'foo')
116 victim = os.fsencode(victim)
117 self.assertIsInstance(victim, bytes)
118 shutil.rmtree(victim)
119
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200120 @support.skip_unless_symlink
121 def test_rmtree_fails_on_symlink(self):
122 tmp = self.mkdtemp()
123 dir_ = os.path.join(tmp, 'dir')
124 os.mkdir(dir_)
125 link = os.path.join(tmp, 'link')
126 os.symlink(dir_, link)
127 self.assertRaises(OSError, shutil.rmtree, link)
128 self.assertTrue(os.path.exists(dir_))
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100129 self.assertTrue(os.path.lexists(link))
130 errors = []
131 def onerror(*args):
132 errors.append(args)
133 shutil.rmtree(link, onerror=onerror)
134 self.assertEqual(len(errors), 1)
135 self.assertIs(errors[0][0], os.path.islink)
136 self.assertEqual(errors[0][1], link)
137 self.assertIsInstance(errors[0][2][1], OSError)
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200138
139 @support.skip_unless_symlink
140 def test_rmtree_works_on_symlinks(self):
141 tmp = self.mkdtemp()
142 dir1 = os.path.join(tmp, 'dir1')
143 dir2 = os.path.join(dir1, 'dir2')
144 dir3 = os.path.join(tmp, 'dir3')
145 for d in dir1, dir2, dir3:
146 os.mkdir(d)
147 file1 = os.path.join(tmp, 'file1')
148 write_file(file1, 'foo')
149 link1 = os.path.join(dir1, 'link1')
150 os.symlink(dir2, link1)
151 link2 = os.path.join(dir1, 'link2')
152 os.symlink(dir3, link2)
153 link3 = os.path.join(dir1, 'link3')
154 os.symlink(file1, link3)
155 # make sure symlinks are removed but not followed
156 shutil.rmtree(dir1)
157 self.assertFalse(os.path.exists(dir1))
158 self.assertTrue(os.path.exists(dir3))
159 self.assertTrue(os.path.exists(file1))
160
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000161 def test_rmtree_errors(self):
162 # filename is guaranteed not to exist
163 filename = tempfile.mktemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100164 self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
165 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100166 shutil.rmtree(filename, ignore_errors=True)
167
168 # existing file
169 tmpdir = self.mkdtemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100170 write_file((tmpdir, "tstfile"), "")
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100171 filename = os.path.join(tmpdir, "tstfile")
Hynek Schlawackb5501102012-12-10 09:11:25 +0100172 with self.assertRaises(NotADirectoryError) as cm:
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100173 shutil.rmtree(filename)
174 self.assertEqual(cm.exception.filename, filename)
175 self.assertTrue(os.path.exists(filename))
Hynek Schlawackb5501102012-12-10 09:11:25 +0100176 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100177 shutil.rmtree(filename, ignore_errors=True)
178 self.assertTrue(os.path.exists(filename))
179 errors = []
180 def onerror(*args):
181 errors.append(args)
182 shutil.rmtree(filename, onerror=onerror)
183 self.assertEqual(len(errors), 2)
184 self.assertIs(errors[0][0], os.listdir)
185 self.assertEqual(errors[0][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100186 self.assertIsInstance(errors[0][2][1], NotADirectoryError)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100187 self.assertEqual(errors[0][2][1].filename, filename)
188 self.assertIs(errors[1][0], os.rmdir)
189 self.assertEqual(errors[1][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100190 self.assertIsInstance(errors[1][2][1], NotADirectoryError)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100191 self.assertEqual(errors[1][2][1].filename, filename)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000192
Johannes Gijsbersb8b09d02004-12-06 20:50:15 +0000193 # See bug #1071513 for why we don't run this on cygwin
194 # and bug #1076467 for why we don't run this as root.
195 if (hasattr(os, 'chmod') and sys.platform[:6] != 'cygwin'
Johannes Gijsbers6b220b02004-12-12 15:52:57 +0000196 and not (hasattr(os, 'geteuid') and os.geteuid() == 0)):
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000197 def test_on_error(self):
198 self.errorState = 0
199 os.mkdir(TESTFN)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200200 self.addCleanup(shutil.rmtree, TESTFN)
201
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200202 self.child_file_path = os.path.join(TESTFN, 'a')
203 self.child_dir_path = os.path.join(TESTFN, 'b')
204 support.create_empty_file(self.child_file_path)
205 os.mkdir(self.child_dir_path)
Tim Peters4590c002004-11-01 02:40:52 +0000206 old_dir_mode = os.stat(TESTFN).st_mode
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200207 old_child_file_mode = os.stat(self.child_file_path).st_mode
208 old_child_dir_mode = os.stat(self.child_dir_path).st_mode
Tim Peters4590c002004-11-01 02:40:52 +0000209 # Make unwritable.
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200210 new_mode = stat.S_IREAD|stat.S_IEXEC
211 os.chmod(self.child_file_path, new_mode)
212 os.chmod(self.child_dir_path, new_mode)
213 os.chmod(TESTFN, new_mode)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000214
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200215 self.addCleanup(os.chmod, TESTFN, old_dir_mode)
216 self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
217 self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
218
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000219 shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
Johannes Gijsbers8e6f2de2004-11-23 09:27:27 +0000220 # Test whether onerror has actually been called.
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200221 self.assertEqual(self.errorState, 3,
222 "Expected call to onerror function did not "
223 "happen.")
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000224
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000225 def check_args_to_onerror(self, func, arg, exc):
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000226 # test_rmtree_errors deliberately runs rmtree
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200227 # on a directory that is chmod 500, which will fail.
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000228 # This function is run when shutil.rmtree fails.
229 # 99.9% of the time it initially fails to remove
230 # a file in the directory, so the first time through
231 # func is os.remove.
232 # However, some Linux machines running ZFS on
233 # FUSE experienced a failure earlier in the process
234 # at os.listdir. The first failure may legally
235 # be either.
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200236 if self.errorState < 2:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200237 if func is os.unlink:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200238 self.assertEqual(arg, self.child_file_path)
239 elif func is os.rmdir:
240 self.assertEqual(arg, self.child_dir_path)
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000241 else:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200242 self.assertIs(func, os.listdir)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200243 self.assertIn(arg, [TESTFN, self.child_dir_path])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000244 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200245 self.errorState += 1
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000246 else:
247 self.assertEqual(func, os.rmdir)
248 self.assertEqual(arg, TESTFN)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000249 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200250 self.errorState = 3
251
252 def test_rmtree_does_not_choke_on_failing_lstat(self):
253 try:
254 orig_lstat = os.lstat
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200255 def raiser(fn, *args, **kwargs):
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200256 if fn != TESTFN:
257 raise OSError()
258 else:
259 return orig_lstat(fn)
260 os.lstat = raiser
261
262 os.mkdir(TESTFN)
263 write_file((TESTFN, 'foo'), 'foo')
264 shutil.rmtree(TESTFN)
265 finally:
266 os.lstat = orig_lstat
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000267
Antoine Pitrou78091e62011-12-29 18:54:15 +0100268 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
269 @support.skip_unless_symlink
270 def test_copymode_follow_symlinks(self):
271 tmp_dir = self.mkdtemp()
272 src = os.path.join(tmp_dir, 'foo')
273 dst = os.path.join(tmp_dir, 'bar')
274 src_link = os.path.join(tmp_dir, 'baz')
275 dst_link = os.path.join(tmp_dir, 'quux')
276 write_file(src, 'foo')
277 write_file(dst, 'foo')
278 os.symlink(src, src_link)
279 os.symlink(dst, dst_link)
280 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
281 # file to file
282 os.chmod(dst, stat.S_IRWXO)
283 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
284 shutil.copymode(src, dst)
285 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
286 # follow src link
287 os.chmod(dst, stat.S_IRWXO)
288 shutil.copymode(src_link, dst)
289 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
290 # follow dst link
291 os.chmod(dst, stat.S_IRWXO)
292 shutil.copymode(src, dst_link)
293 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
294 # follow both links
295 os.chmod(dst, stat.S_IRWXO)
296 shutil.copymode(src_link, dst)
297 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
298
299 @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
300 @support.skip_unless_symlink
301 def test_copymode_symlink_to_symlink(self):
302 tmp_dir = self.mkdtemp()
303 src = os.path.join(tmp_dir, 'foo')
304 dst = os.path.join(tmp_dir, 'bar')
305 src_link = os.path.join(tmp_dir, 'baz')
306 dst_link = os.path.join(tmp_dir, 'quux')
307 write_file(src, 'foo')
308 write_file(dst, 'foo')
309 os.symlink(src, src_link)
310 os.symlink(dst, dst_link)
311 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
312 os.chmod(dst, stat.S_IRWXU)
313 os.lchmod(src_link, stat.S_IRWXO|stat.S_IRWXG)
314 # link to link
315 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700316 shutil.copymode(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100317 self.assertEqual(os.lstat(src_link).st_mode,
318 os.lstat(dst_link).st_mode)
319 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
320 # src link - use chmod
321 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700322 shutil.copymode(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100323 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
324 # dst link - use chmod
325 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700326 shutil.copymode(src, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100327 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
328
329 @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing')
330 @support.skip_unless_symlink
331 def test_copymode_symlink_to_symlink_wo_lchmod(self):
332 tmp_dir = self.mkdtemp()
333 src = os.path.join(tmp_dir, 'foo')
334 dst = os.path.join(tmp_dir, 'bar')
335 src_link = os.path.join(tmp_dir, 'baz')
336 dst_link = os.path.join(tmp_dir, 'quux')
337 write_file(src, 'foo')
338 write_file(dst, 'foo')
339 os.symlink(src, src_link)
340 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700341 shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail
Antoine Pitrou78091e62011-12-29 18:54:15 +0100342
343 @support.skip_unless_symlink
344 def test_copystat_symlinks(self):
345 tmp_dir = self.mkdtemp()
346 src = os.path.join(tmp_dir, 'foo')
347 dst = os.path.join(tmp_dir, 'bar')
348 src_link = os.path.join(tmp_dir, 'baz')
349 dst_link = os.path.join(tmp_dir, 'qux')
350 write_file(src, 'foo')
351 src_stat = os.stat(src)
352 os.utime(src, (src_stat.st_atime,
353 src_stat.st_mtime - 42.0)) # ensure different mtimes
354 write_file(dst, 'bar')
355 self.assertNotEqual(os.stat(src).st_mtime, os.stat(dst).st_mtime)
356 os.symlink(src, src_link)
357 os.symlink(dst, dst_link)
358 if hasattr(os, 'lchmod'):
359 os.lchmod(src_link, stat.S_IRWXO)
360 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
361 os.lchflags(src_link, stat.UF_NODUMP)
362 src_link_stat = os.lstat(src_link)
363 # follow
364 if hasattr(os, 'lchmod'):
Larry Hastingsb4038062012-07-15 10:57:38 -0700365 shutil.copystat(src_link, dst_link, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100366 self.assertNotEqual(src_link_stat.st_mode, os.stat(dst).st_mode)
367 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700368 shutil.copystat(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100369 dst_link_stat = os.lstat(dst_link)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700370 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100371 for attr in 'st_atime', 'st_mtime':
372 # The modification times may be truncated in the new file.
373 self.assertLessEqual(getattr(src_link_stat, attr),
374 getattr(dst_link_stat, attr) + 1)
375 if hasattr(os, 'lchmod'):
376 self.assertEqual(src_link_stat.st_mode, dst_link_stat.st_mode)
377 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
378 self.assertEqual(src_link_stat.st_flags, dst_link_stat.st_flags)
379 # tell to follow but dst is not a link
Larry Hastingsb4038062012-07-15 10:57:38 -0700380 shutil.copystat(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100381 self.assertTrue(abs(os.stat(src).st_mtime - os.stat(dst).st_mtime) <
382 00000.1)
383
Ned Deilybaf75712012-05-10 17:05:19 -0700384 @unittest.skipUnless(hasattr(os, 'chflags') and
385 hasattr(errno, 'EOPNOTSUPP') and
386 hasattr(errno, 'ENOTSUP'),
387 "requires os.chflags, EOPNOTSUPP & ENOTSUP")
388 def test_copystat_handles_harmless_chflags_errors(self):
389 tmpdir = self.mkdtemp()
390 file1 = os.path.join(tmpdir, 'file1')
391 file2 = os.path.join(tmpdir, 'file2')
392 write_file(file1, 'xxx')
393 write_file(file2, 'xxx')
394
395 def make_chflags_raiser(err):
396 ex = OSError()
397
Larry Hastings90867a52012-06-22 17:01:41 -0700398 def _chflags_raiser(path, flags, *, follow_symlinks=True):
Ned Deilybaf75712012-05-10 17:05:19 -0700399 ex.errno = err
400 raise ex
401 return _chflags_raiser
402 old_chflags = os.chflags
403 try:
404 for err in errno.EOPNOTSUPP, errno.ENOTSUP:
405 os.chflags = make_chflags_raiser(err)
406 shutil.copystat(file1, file2)
407 # assert others errors break it
408 os.chflags = make_chflags_raiser(errno.EOPNOTSUPP + errno.ENOTSUP)
409 self.assertRaises(OSError, shutil.copystat, file1, file2)
410 finally:
411 os.chflags = old_chflags
412
Antoine Pitrou424246f2012-05-12 19:02:01 +0200413 @support.skip_unless_xattr
414 def test_copyxattr(self):
415 tmp_dir = self.mkdtemp()
416 src = os.path.join(tmp_dir, 'foo')
417 write_file(src, 'foo')
418 dst = os.path.join(tmp_dir, 'bar')
419 write_file(dst, 'bar')
420
421 # no xattr == no problem
422 shutil._copyxattr(src, dst)
423 # common case
424 os.setxattr(src, 'user.foo', b'42')
425 os.setxattr(src, 'user.bar', b'43')
426 shutil._copyxattr(src, dst)
427 self.assertEqual(os.listxattr(src), os.listxattr(dst))
428 self.assertEqual(
429 os.getxattr(src, 'user.foo'),
430 os.getxattr(dst, 'user.foo'))
431 # check errors don't affect other attrs
432 os.remove(dst)
433 write_file(dst, 'bar')
434 os_error = OSError(errno.EPERM, 'EPERM')
435
Larry Hastings9cf065c2012-06-22 16:30:09 -0700436 def _raise_on_user_foo(fname, attr, val, **kwargs):
Antoine Pitrou424246f2012-05-12 19:02:01 +0200437 if attr == 'user.foo':
438 raise os_error
439 else:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700440 orig_setxattr(fname, attr, val, **kwargs)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200441 try:
442 orig_setxattr = os.setxattr
443 os.setxattr = _raise_on_user_foo
444 shutil._copyxattr(src, dst)
Antoine Pitrou61597d32012-05-12 23:37:35 +0200445 self.assertIn('user.bar', os.listxattr(dst))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200446 finally:
447 os.setxattr = orig_setxattr
448
Larry Hastingsad5ae042012-07-14 17:55:11 -0700449 # test that shutil.copystat copies xattrs
450 src = os.path.join(tmp_dir, 'the_original')
451 write_file(src, src)
452 os.setxattr(src, 'user.the_value', b'fiddly')
453 dst = os.path.join(tmp_dir, 'the_copy')
454 write_file(dst, dst)
455 shutil.copystat(src, dst)
Hynek Schlawackc2d481f2012-07-16 17:11:10 +0200456 self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700457
Antoine Pitrou424246f2012-05-12 19:02:01 +0200458 @support.skip_unless_symlink
459 @support.skip_unless_xattr
460 @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,
461 'root privileges required')
462 def test_copyxattr_symlinks(self):
463 # On Linux, it's only possible to access non-user xattr for symlinks;
464 # which in turn require root privileges. This test should be expanded
465 # as soon as other platforms gain support for extended attributes.
466 tmp_dir = self.mkdtemp()
467 src = os.path.join(tmp_dir, 'foo')
468 src_link = os.path.join(tmp_dir, 'baz')
469 write_file(src, 'foo')
470 os.symlink(src, src_link)
471 os.setxattr(src, 'trusted.foo', b'42')
Larry Hastings9cf065c2012-06-22 16:30:09 -0700472 os.setxattr(src_link, 'trusted.foo', b'43', follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200473 dst = os.path.join(tmp_dir, 'bar')
474 dst_link = os.path.join(tmp_dir, 'qux')
475 write_file(dst, 'bar')
476 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700477 shutil._copyxattr(src_link, dst_link, follow_symlinks=False)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700478 self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43')
Antoine Pitrou424246f2012-05-12 19:02:01 +0200479 self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo')
Larry Hastingsb4038062012-07-15 10:57:38 -0700480 shutil._copyxattr(src_link, dst, follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200481 self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43')
482
Antoine Pitrou78091e62011-12-29 18:54:15 +0100483 @support.skip_unless_symlink
484 def test_copy_symlinks(self):
485 tmp_dir = self.mkdtemp()
486 src = os.path.join(tmp_dir, 'foo')
487 dst = os.path.join(tmp_dir, 'bar')
488 src_link = os.path.join(tmp_dir, 'baz')
489 write_file(src, 'foo')
490 os.symlink(src, src_link)
491 if hasattr(os, 'lchmod'):
492 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
493 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700494 shutil.copy(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100495 self.assertFalse(os.path.islink(dst))
496 self.assertEqual(read_file(src), read_file(dst))
497 os.remove(dst)
498 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700499 shutil.copy(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100500 self.assertTrue(os.path.islink(dst))
501 self.assertEqual(os.readlink(dst), os.readlink(src_link))
502 if hasattr(os, 'lchmod'):
503 self.assertEqual(os.lstat(src_link).st_mode,
504 os.lstat(dst).st_mode)
505
506 @support.skip_unless_symlink
507 def test_copy2_symlinks(self):
508 tmp_dir = self.mkdtemp()
509 src = os.path.join(tmp_dir, 'foo')
510 dst = os.path.join(tmp_dir, 'bar')
511 src_link = os.path.join(tmp_dir, 'baz')
512 write_file(src, 'foo')
513 os.symlink(src, src_link)
514 if hasattr(os, 'lchmod'):
515 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
516 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
517 os.lchflags(src_link, stat.UF_NODUMP)
518 src_stat = os.stat(src)
519 src_link_stat = os.lstat(src_link)
520 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700521 shutil.copy2(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100522 self.assertFalse(os.path.islink(dst))
523 self.assertEqual(read_file(src), read_file(dst))
524 os.remove(dst)
525 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700526 shutil.copy2(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100527 self.assertTrue(os.path.islink(dst))
528 self.assertEqual(os.readlink(dst), os.readlink(src_link))
529 dst_stat = os.lstat(dst)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700530 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100531 for attr in 'st_atime', 'st_mtime':
532 # The modification times may be truncated in the new file.
533 self.assertLessEqual(getattr(src_link_stat, attr),
534 getattr(dst_stat, attr) + 1)
535 if hasattr(os, 'lchmod'):
536 self.assertEqual(src_link_stat.st_mode, dst_stat.st_mode)
537 self.assertNotEqual(src_stat.st_mode, dst_stat.st_mode)
538 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
539 self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags)
540
Antoine Pitrou424246f2012-05-12 19:02:01 +0200541 @support.skip_unless_xattr
542 def test_copy2_xattr(self):
543 tmp_dir = self.mkdtemp()
544 src = os.path.join(tmp_dir, 'foo')
545 dst = os.path.join(tmp_dir, 'bar')
546 write_file(src, 'foo')
547 os.setxattr(src, 'user.foo', b'42')
548 shutil.copy2(src, dst)
549 self.assertEqual(
550 os.getxattr(src, 'user.foo'),
551 os.getxattr(dst, 'user.foo'))
552 os.remove(dst)
553
Antoine Pitrou78091e62011-12-29 18:54:15 +0100554 @support.skip_unless_symlink
555 def test_copyfile_symlinks(self):
556 tmp_dir = self.mkdtemp()
557 src = os.path.join(tmp_dir, 'src')
558 dst = os.path.join(tmp_dir, 'dst')
559 dst_link = os.path.join(tmp_dir, 'dst_link')
560 link = os.path.join(tmp_dir, 'link')
561 write_file(src, 'foo')
562 os.symlink(src, link)
563 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700564 shutil.copyfile(link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100565 self.assertTrue(os.path.islink(dst_link))
566 self.assertEqual(os.readlink(link), os.readlink(dst_link))
567 # follow
568 shutil.copyfile(link, dst)
569 self.assertFalse(os.path.islink(dst))
570
Hynek Schlawack2100b422012-06-23 20:28:32 +0200571 def test_rmtree_uses_safe_fd_version_if_available(self):
Hynek Schlawackd0f6e0a2012-06-29 08:28:20 +0200572 _use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
573 os.supports_dir_fd and
574 os.listdir in os.supports_fd and
575 os.stat in os.supports_follow_symlinks)
576 if _use_fd_functions:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200577 self.assertTrue(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000578 self.assertTrue(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200579 tmp_dir = self.mkdtemp()
580 d = os.path.join(tmp_dir, 'a')
581 os.mkdir(d)
582 try:
583 real_rmtree = shutil._rmtree_safe_fd
584 class Called(Exception): pass
585 def _raiser(*args, **kwargs):
586 raise Called
587 shutil._rmtree_safe_fd = _raiser
588 self.assertRaises(Called, shutil.rmtree, d)
589 finally:
590 shutil._rmtree_safe_fd = real_rmtree
591 else:
592 self.assertFalse(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000593 self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200594
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000595 def test_rmtree_dont_delete_file(self):
596 # When called on a file instead of a directory, don't delete it.
597 handle, path = tempfile.mkstemp()
Victor Stinnerbf816222011-06-30 23:25:47 +0200598 os.close(handle)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200599 self.assertRaises(NotADirectoryError, shutil.rmtree, path)
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000600 os.remove(path)
601
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000602 def test_copytree_simple(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000603 src_dir = tempfile.mkdtemp()
604 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200605 self.addCleanup(shutil.rmtree, src_dir)
606 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
607 write_file((src_dir, 'test.txt'), '123')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000608 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200609 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000610
Éric Araujoa7e33a12011-08-12 19:51:35 +0200611 shutil.copytree(src_dir, dst_dir)
612 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test.txt')))
613 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'test_dir')))
614 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test_dir',
615 'test.txt')))
616 actual = read_file((dst_dir, 'test.txt'))
617 self.assertEqual(actual, '123')
618 actual = read_file((dst_dir, 'test_dir', 'test.txt'))
619 self.assertEqual(actual, '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000620
Antoine Pitrou78091e62011-12-29 18:54:15 +0100621 @support.skip_unless_symlink
622 def test_copytree_symlinks(self):
623 tmp_dir = self.mkdtemp()
624 src_dir = os.path.join(tmp_dir, 'src')
625 dst_dir = os.path.join(tmp_dir, 'dst')
626 sub_dir = os.path.join(src_dir, 'sub')
627 os.mkdir(src_dir)
628 os.mkdir(sub_dir)
629 write_file((src_dir, 'file.txt'), 'foo')
630 src_link = os.path.join(sub_dir, 'link')
631 dst_link = os.path.join(dst_dir, 'sub/link')
632 os.symlink(os.path.join(src_dir, 'file.txt'),
633 src_link)
634 if hasattr(os, 'lchmod'):
635 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
636 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
637 os.lchflags(src_link, stat.UF_NODUMP)
638 src_stat = os.lstat(src_link)
639 shutil.copytree(src_dir, dst_dir, symlinks=True)
640 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'sub', 'link')))
641 self.assertEqual(os.readlink(os.path.join(dst_dir, 'sub', 'link')),
642 os.path.join(src_dir, 'file.txt'))
643 dst_stat = os.lstat(dst_link)
644 if hasattr(os, 'lchmod'):
645 self.assertEqual(dst_stat.st_mode, src_stat.st_mode)
646 if hasattr(os, 'lchflags'):
647 self.assertEqual(dst_stat.st_flags, src_stat.st_flags)
648
Georg Brandl2ee470f2008-07-16 12:55:28 +0000649 def test_copytree_with_exclude(self):
Georg Brandl2ee470f2008-07-16 12:55:28 +0000650 # creating data
651 join = os.path.join
652 exists = os.path.exists
653 src_dir = tempfile.mkdtemp()
Georg Brandl2ee470f2008-07-16 12:55:28 +0000654 try:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000655 dst_dir = join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200656 write_file((src_dir, 'test.txt'), '123')
657 write_file((src_dir, 'test.tmp'), '123')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000658 os.mkdir(join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200659 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000660 os.mkdir(join(src_dir, 'test_dir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200661 write_file((src_dir, 'test_dir2', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000662 os.mkdir(join(src_dir, 'test_dir2', 'subdir'))
663 os.mkdir(join(src_dir, 'test_dir2', 'subdir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200664 write_file((src_dir, 'test_dir2', 'subdir', 'test.txt'), '456')
665 write_file((src_dir, 'test_dir2', 'subdir2', 'test.py'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000666
667 # testing glob-like patterns
668 try:
669 patterns = shutil.ignore_patterns('*.tmp', 'test_dir2')
670 shutil.copytree(src_dir, dst_dir, ignore=patterns)
671 # checking the result: some elements should not be copied
672 self.assertTrue(exists(join(dst_dir, 'test.txt')))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200673 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
674 self.assertFalse(exists(join(dst_dir, 'test_dir2')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000675 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200676 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000677 try:
678 patterns = shutil.ignore_patterns('*.tmp', 'subdir*')
679 shutil.copytree(src_dir, dst_dir, ignore=patterns)
680 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200681 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
682 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2')))
683 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000684 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200685 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000686
687 # testing callable-style
688 try:
689 def _filter(src, names):
690 res = []
691 for name in names:
692 path = os.path.join(src, name)
693
694 if (os.path.isdir(path) and
695 path.split()[-1] == 'subdir'):
696 res.append(name)
697 elif os.path.splitext(path)[-1] in ('.py'):
698 res.append(name)
699 return res
700
701 shutil.copytree(src_dir, dst_dir, ignore=_filter)
702
703 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200704 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2',
705 'test.py')))
706 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000707
708 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200709 shutil.rmtree(dst_dir)
Georg Brandl2ee470f2008-07-16 12:55:28 +0000710 finally:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000711 shutil.rmtree(src_dir)
712 shutil.rmtree(os.path.dirname(dst_dir))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000713
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000714 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000715 def test_dont_copy_file_onto_link_to_itself(self):
Georg Brandl724d0892010-12-05 07:51:39 +0000716 # Temporarily disable test on Windows.
717 if os.name == 'nt':
718 return
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000719 # bug 851123.
720 os.mkdir(TESTFN)
721 src = os.path.join(TESTFN, 'cheese')
722 dst = os.path.join(TESTFN, 'shop')
723 try:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000724 with open(src, 'w') as f:
725 f.write('cheddar')
726 os.link(src, dst)
Hynek Schlawack26fe37d2012-07-19 21:41:02 +0200727 self.assertRaises(shutil.Error, shutil.copyfile, src, dst)
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000728 with open(src, 'r') as f:
729 self.assertEqual(f.read(), 'cheddar')
730 os.remove(dst)
731 finally:
732 shutil.rmtree(TESTFN, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000733
Brian Curtin3b4499c2010-12-28 14:31:47 +0000734 @support.skip_unless_symlink
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000735 def test_dont_copy_file_onto_symlink_to_itself(self):
736 # bug 851123.
737 os.mkdir(TESTFN)
738 src = os.path.join(TESTFN, 'cheese')
739 dst = os.path.join(TESTFN, 'shop')
740 try:
741 with open(src, 'w') as f:
742 f.write('cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000743 # Using `src` here would mean we end up with a symlink pointing
744 # to TESTFN/TESTFN/cheese, while it should point at
745 # TESTFN/cheese.
746 os.symlink('cheese', dst)
Hynek Schlawack26fe37d2012-07-19 21:41:02 +0200747 self.assertRaises(shutil.Error, shutil.copyfile, src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000748 with open(src, 'r') as f:
749 self.assertEqual(f.read(), 'cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000750 os.remove(dst)
751 finally:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000752 shutil.rmtree(TESTFN, ignore_errors=True)
Johannes Gijsbers68128712004-08-14 13:57:08 +0000753
Brian Curtin3b4499c2010-12-28 14:31:47 +0000754 @support.skip_unless_symlink
Brian Curtind40e6f72010-07-08 21:39:08 +0000755 def test_rmtree_on_symlink(self):
756 # bug 1669.
757 os.mkdir(TESTFN)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000758 try:
Brian Curtind40e6f72010-07-08 21:39:08 +0000759 src = os.path.join(TESTFN, 'cheese')
760 dst = os.path.join(TESTFN, 'shop')
761 os.mkdir(src)
762 os.symlink(src, dst)
763 self.assertRaises(OSError, shutil.rmtree, dst)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200764 shutil.rmtree(dst, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000765 finally:
Brian Curtind40e6f72010-07-08 21:39:08 +0000766 shutil.rmtree(TESTFN, ignore_errors=True)
767
768 if hasattr(os, "mkfifo"):
769 # Issue #3002: copyfile and copytree block indefinitely on named pipes
770 def test_copyfile_named_pipe(self):
771 os.mkfifo(TESTFN)
772 try:
773 self.assertRaises(shutil.SpecialFileError,
774 shutil.copyfile, TESTFN, TESTFN2)
775 self.assertRaises(shutil.SpecialFileError,
776 shutil.copyfile, __file__, TESTFN)
777 finally:
778 os.remove(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000779
Brian Curtin3b4499c2010-12-28 14:31:47 +0000780 @support.skip_unless_symlink
Brian Curtin52173d42010-12-02 18:29:18 +0000781 def test_copytree_named_pipe(self):
782 os.mkdir(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000783 try:
Brian Curtin52173d42010-12-02 18:29:18 +0000784 subdir = os.path.join(TESTFN, "subdir")
785 os.mkdir(subdir)
786 pipe = os.path.join(subdir, "mypipe")
787 os.mkfifo(pipe)
788 try:
789 shutil.copytree(TESTFN, TESTFN2)
790 except shutil.Error as e:
791 errors = e.args[0]
792 self.assertEqual(len(errors), 1)
793 src, dst, error_msg = errors[0]
794 self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
795 else:
796 self.fail("shutil.Error should have been raised")
797 finally:
798 shutil.rmtree(TESTFN, ignore_errors=True)
799 shutil.rmtree(TESTFN2, ignore_errors=True)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000800
Tarek Ziadé5340db32010-04-19 22:30:51 +0000801 def test_copytree_special_func(self):
802
803 src_dir = self.mkdtemp()
804 dst_dir = os.path.join(self.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200805 write_file((src_dir, 'test.txt'), '123')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000806 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200807 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000808
809 copied = []
810 def _copy(src, dst):
811 copied.append((src, dst))
812
813 shutil.copytree(src_dir, dst_dir, copy_function=_copy)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000814 self.assertEqual(len(copied), 2)
Tarek Ziadé5340db32010-04-19 22:30:51 +0000815
Brian Curtin3b4499c2010-12-28 14:31:47 +0000816 @support.skip_unless_symlink
Tarek Ziadéfb437512010-04-20 08:57:33 +0000817 def test_copytree_dangling_symlinks(self):
818
819 # a dangling symlink raises an error at the end
820 src_dir = self.mkdtemp()
821 dst_dir = os.path.join(self.mkdtemp(), 'destination')
822 os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt'))
823 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200824 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadéfb437512010-04-20 08:57:33 +0000825 self.assertRaises(Error, shutil.copytree, src_dir, dst_dir)
826
827 # a dangling symlink is ignored with the proper flag
828 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
829 shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)
830 self.assertNotIn('test.txt', os.listdir(dst_dir))
831
832 # a dangling symlink is copied if symlinks=True
833 dst_dir = os.path.join(self.mkdtemp(), 'destination3')
834 shutil.copytree(src_dir, dst_dir, symlinks=True)
835 self.assertIn('test.txt', os.listdir(dst_dir))
836
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400837 def _copy_file(self, method):
838 fname = 'test.txt'
839 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200840 write_file((tmpdir, fname), 'xxx')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400841 file1 = os.path.join(tmpdir, fname)
842 tmpdir2 = self.mkdtemp()
843 method(file1, tmpdir2)
844 file2 = os.path.join(tmpdir2, fname)
845 return (file1, file2)
846
847 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
848 def test_copy(self):
849 # Ensure that the copied file exists and has the same mode bits.
850 file1, file2 = self._copy_file(shutil.copy)
851 self.assertTrue(os.path.exists(file2))
852 self.assertEqual(os.stat(file1).st_mode, os.stat(file2).st_mode)
853
854 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
Senthil Kumaran0c2dba52011-07-03 18:21:38 -0700855 @unittest.skipUnless(hasattr(os, 'utime'), 'requires os.utime')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400856 def test_copy2(self):
857 # Ensure that the copied file exists and has the same mode and
858 # modification time bits.
859 file1, file2 = self._copy_file(shutil.copy2)
860 self.assertTrue(os.path.exists(file2))
861 file1_stat = os.stat(file1)
862 file2_stat = os.stat(file2)
863 self.assertEqual(file1_stat.st_mode, file2_stat.st_mode)
864 for attr in 'st_atime', 'st_mtime':
865 # The modification times may be truncated in the new file.
866 self.assertLessEqual(getattr(file1_stat, attr),
867 getattr(file2_stat, attr) + 1)
868 if hasattr(os, 'chflags') and hasattr(file1_stat, 'st_flags'):
869 self.assertEqual(getattr(file1_stat, 'st_flags'),
870 getattr(file2_stat, 'st_flags'))
871
Ezio Melotti975077a2011-05-19 22:03:22 +0300872 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000873 def test_make_tarball(self):
874 # creating something to tar
875 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200876 write_file((tmpdir, 'file1'), 'xxx')
877 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000878 os.mkdir(os.path.join(tmpdir, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200879 write_file((tmpdir, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000880
881 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400882 # force shutil to create the directory
883 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000884 unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
885 "source and target should be on same drive")
886
887 base_name = os.path.join(tmpdir2, 'archive')
888
889 # working with relative paths to avoid tar warnings
890 old_dir = os.getcwd()
891 os.chdir(tmpdir)
892 try:
893 _make_tarball(splitdrive(base_name)[1], '.')
894 finally:
895 os.chdir(old_dir)
896
897 # check if the compressed tarball was created
898 tarball = base_name + '.tar.gz'
899 self.assertTrue(os.path.exists(tarball))
900
901 # trying an uncompressed one
902 base_name = os.path.join(tmpdir2, 'archive')
903 old_dir = os.getcwd()
904 os.chdir(tmpdir)
905 try:
906 _make_tarball(splitdrive(base_name)[1], '.', compress=None)
907 finally:
908 os.chdir(old_dir)
909 tarball = base_name + '.tar'
910 self.assertTrue(os.path.exists(tarball))
911
912 def _tarinfo(self, path):
913 tar = tarfile.open(path)
914 try:
915 names = tar.getnames()
916 names.sort()
917 return tuple(names)
918 finally:
919 tar.close()
920
921 def _create_files(self):
922 # creating something to tar
923 tmpdir = self.mkdtemp()
924 dist = os.path.join(tmpdir, 'dist')
925 os.mkdir(dist)
Éric Araujoa7e33a12011-08-12 19:51:35 +0200926 write_file((dist, 'file1'), 'xxx')
927 write_file((dist, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000928 os.mkdir(os.path.join(dist, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200929 write_file((dist, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000930 os.mkdir(os.path.join(dist, 'sub2'))
931 tmpdir2 = self.mkdtemp()
932 base_name = os.path.join(tmpdir2, 'archive')
933 return tmpdir, tmpdir2, base_name
934
Ezio Melotti975077a2011-05-19 22:03:22 +0300935 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000936 @unittest.skipUnless(find_executable('tar') and find_executable('gzip'),
937 'Need the tar command to run')
938 def test_tarfile_vs_tar(self):
939 tmpdir, tmpdir2, base_name = self._create_files()
940 old_dir = os.getcwd()
941 os.chdir(tmpdir)
942 try:
943 _make_tarball(base_name, 'dist')
944 finally:
945 os.chdir(old_dir)
946
947 # check if the compressed tarball was created
948 tarball = base_name + '.tar.gz'
949 self.assertTrue(os.path.exists(tarball))
950
951 # now create another tarball using `tar`
952 tarball2 = os.path.join(tmpdir, 'archive2.tar.gz')
953 tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist']
954 gzip_cmd = ['gzip', '-f9', 'archive2.tar']
955 old_dir = os.getcwd()
956 os.chdir(tmpdir)
957 try:
958 with captured_stdout() as s:
959 spawn(tar_cmd)
960 spawn(gzip_cmd)
961 finally:
962 os.chdir(old_dir)
963
964 self.assertTrue(os.path.exists(tarball2))
965 # let's compare both tarballs
Ezio Melottib3aedd42010-11-20 19:04:17 +0000966 self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +0000967
968 # trying an uncompressed one
969 base_name = os.path.join(tmpdir2, 'archive')
970 old_dir = os.getcwd()
971 os.chdir(tmpdir)
972 try:
973 _make_tarball(base_name, 'dist', compress=None)
974 finally:
975 os.chdir(old_dir)
976 tarball = base_name + '.tar'
977 self.assertTrue(os.path.exists(tarball))
978
979 # now for a dry_run
980 base_name = os.path.join(tmpdir2, 'archive')
981 old_dir = os.getcwd()
982 os.chdir(tmpdir)
983 try:
984 _make_tarball(base_name, 'dist', compress=None, dry_run=True)
985 finally:
986 os.chdir(old_dir)
987 tarball = base_name + '.tar'
988 self.assertTrue(os.path.exists(tarball))
989
Ezio Melotti975077a2011-05-19 22:03:22 +0300990 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000991 @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
992 def test_make_zipfile(self):
993 # creating something to tar
994 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200995 write_file((tmpdir, 'file1'), 'xxx')
996 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000997
998 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400999 # force shutil to create the directory
1000 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001001 base_name = os.path.join(tmpdir2, 'archive')
1002 _make_zipfile(base_name, tmpdir)
1003
1004 # check if the compressed tarball was created
1005 tarball = base_name + '.zip'
Éric Araujo1c505492010-11-06 02:12:51 +00001006 self.assertTrue(os.path.exists(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001007
1008
1009 def test_make_archive(self):
1010 tmpdir = self.mkdtemp()
1011 base_name = os.path.join(tmpdir, 'archive')
1012 self.assertRaises(ValueError, make_archive, base_name, 'xxx')
1013
Ezio Melotti975077a2011-05-19 22:03:22 +03001014 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001015 def test_make_archive_owner_group(self):
1016 # testing make_archive with owner and group, with various combinations
1017 # this works even if there's not gid/uid support
1018 if UID_GID_SUPPORT:
1019 group = grp.getgrgid(0)[0]
1020 owner = pwd.getpwuid(0)[0]
1021 else:
1022 group = owner = 'root'
1023
1024 base_dir, root_dir, base_name = self._create_files()
1025 base_name = os.path.join(self.mkdtemp() , 'archive')
1026 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
1027 group=group)
1028 self.assertTrue(os.path.exists(res))
1029
1030 res = make_archive(base_name, 'zip', root_dir, base_dir)
1031 self.assertTrue(os.path.exists(res))
1032
1033 res = make_archive(base_name, 'tar', root_dir, base_dir,
1034 owner=owner, group=group)
1035 self.assertTrue(os.path.exists(res))
1036
1037 res = make_archive(base_name, 'tar', root_dir, base_dir,
1038 owner='kjhkjhkjg', group='oihohoh')
1039 self.assertTrue(os.path.exists(res))
1040
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001041
Ezio Melotti975077a2011-05-19 22:03:22 +03001042 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001043 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1044 def test_tarfile_root_owner(self):
1045 tmpdir, tmpdir2, base_name = self._create_files()
1046 old_dir = os.getcwd()
1047 os.chdir(tmpdir)
1048 group = grp.getgrgid(0)[0]
1049 owner = pwd.getpwuid(0)[0]
1050 try:
1051 archive_name = _make_tarball(base_name, 'dist', compress=None,
1052 owner=owner, group=group)
1053 finally:
1054 os.chdir(old_dir)
1055
1056 # check if the compressed tarball was created
1057 self.assertTrue(os.path.exists(archive_name))
1058
1059 # now checks the rights
1060 archive = tarfile.open(archive_name)
1061 try:
1062 for member in archive.getmembers():
Ezio Melottib3aedd42010-11-20 19:04:17 +00001063 self.assertEqual(member.uid, 0)
1064 self.assertEqual(member.gid, 0)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001065 finally:
1066 archive.close()
1067
1068 def test_make_archive_cwd(self):
1069 current_dir = os.getcwd()
1070 def _breaks(*args, **kw):
1071 raise RuntimeError()
1072
1073 register_archive_format('xxx', _breaks, [], 'xxx file')
1074 try:
1075 try:
1076 make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
1077 except Exception:
1078 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +00001079 self.assertEqual(os.getcwd(), current_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001080 finally:
1081 unregister_archive_format('xxx')
1082
1083 def test_register_archive_format(self):
1084
1085 self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
1086 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1087 1)
1088 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1089 [(1, 2), (1, 2, 3)])
1090
1091 register_archive_format('xxx', lambda: x, [(1, 2)], 'xxx file')
1092 formats = [name for name, params in get_archive_formats()]
1093 self.assertIn('xxx', formats)
1094
1095 unregister_archive_format('xxx')
1096 formats = [name for name, params in get_archive_formats()]
1097 self.assertNotIn('xxx', formats)
1098
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001099 def _compare_dirs(self, dir1, dir2):
1100 # check that dir1 and dir2 are equivalent,
1101 # return the diff
1102 diff = []
1103 for root, dirs, files in os.walk(dir1):
1104 for file_ in files:
1105 path = os.path.join(root, file_)
1106 target_path = os.path.join(dir2, os.path.split(path)[-1])
1107 if not os.path.exists(target_path):
1108 diff.append(file_)
1109 return diff
1110
Ezio Melotti975077a2011-05-19 22:03:22 +03001111 @requires_zlib
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001112 def test_unpack_archive(self):
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001113 formats = ['tar', 'gztar', 'zip']
1114 if BZ2_SUPPORTED:
1115 formats.append('bztar')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001116
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001117 for format in formats:
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001118 tmpdir = self.mkdtemp()
1119 base_dir, root_dir, base_name = self._create_files()
1120 tmpdir2 = self.mkdtemp()
1121 filename = make_archive(base_name, format, root_dir, base_dir)
1122
1123 # let's try to unpack it now
1124 unpack_archive(filename, tmpdir2)
1125 diff = self._compare_dirs(tmpdir, tmpdir2)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001126 self.assertEqual(diff, [])
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001127
Nick Coghlanabf202d2011-03-16 13:52:20 -04001128 # and again, this time with the format specified
1129 tmpdir3 = self.mkdtemp()
1130 unpack_archive(filename, tmpdir3, format=format)
1131 diff = self._compare_dirs(tmpdir, tmpdir3)
1132 self.assertEqual(diff, [])
1133 self.assertRaises(shutil.ReadError, unpack_archive, TESTFN)
1134 self.assertRaises(ValueError, unpack_archive, TESTFN, format='xxx')
1135
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001136 def test_unpack_registery(self):
1137
1138 formats = get_unpack_formats()
1139
1140 def _boo(filename, extract_dir, extra):
Ezio Melottib3aedd42010-11-20 19:04:17 +00001141 self.assertEqual(extra, 1)
1142 self.assertEqual(filename, 'stuff.boo')
1143 self.assertEqual(extract_dir, 'xx')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001144
1145 register_unpack_format('Boo', ['.boo', '.b2'], _boo, [('extra', 1)])
1146 unpack_archive('stuff.boo', 'xx')
1147
1148 # trying to register a .boo unpacker again
1149 self.assertRaises(RegistryError, register_unpack_format, 'Boo2',
1150 ['.boo'], _boo)
1151
1152 # should work now
1153 unregister_unpack_format('Boo')
1154 register_unpack_format('Boo2', ['.boo'], _boo)
1155 self.assertIn(('Boo2', ['.boo'], ''), get_unpack_formats())
1156 self.assertNotIn(('Boo', ['.boo'], ''), get_unpack_formats())
1157
1158 # let's leave a clean state
1159 unregister_unpack_format('Boo2')
Ezio Melottib3aedd42010-11-20 19:04:17 +00001160 self.assertEqual(get_unpack_formats(), formats)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001161
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001162 @unittest.skipUnless(hasattr(shutil, 'disk_usage'),
1163 "disk_usage not available on this platform")
1164 def test_disk_usage(self):
1165 usage = shutil.disk_usage(os.getcwd())
Éric Araujo2ee61882011-07-02 16:45:45 +02001166 self.assertGreater(usage.total, 0)
1167 self.assertGreater(usage.used, 0)
1168 self.assertGreaterEqual(usage.free, 0)
1169 self.assertGreaterEqual(usage.total, usage.used)
1170 self.assertGreater(usage.total, usage.free)
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001171
Sandro Tosid902a142011-08-22 23:28:27 +02001172 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1173 @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
1174 def test_chown(self):
1175
1176 # cleaned-up automatically by TestShutil.tearDown method
1177 dirname = self.mkdtemp()
1178 filename = tempfile.mktemp(dir=dirname)
1179 write_file(filename, 'testing chown function')
1180
1181 with self.assertRaises(ValueError):
1182 shutil.chown(filename)
1183
1184 with self.assertRaises(LookupError):
1185 shutil.chown(filename, user='non-exising username')
1186
1187 with self.assertRaises(LookupError):
1188 shutil.chown(filename, group='non-exising groupname')
1189
1190 with self.assertRaises(TypeError):
1191 shutil.chown(filename, b'spam')
1192
1193 with self.assertRaises(TypeError):
1194 shutil.chown(filename, 3.14)
1195
1196 uid = os.getuid()
1197 gid = os.getgid()
1198
1199 def check_chown(path, uid=None, gid=None):
1200 s = os.stat(filename)
1201 if uid is not None:
1202 self.assertEqual(uid, s.st_uid)
1203 if gid is not None:
1204 self.assertEqual(gid, s.st_gid)
1205
1206 shutil.chown(filename, uid, gid)
1207 check_chown(filename, uid, gid)
1208 shutil.chown(filename, uid)
1209 check_chown(filename, uid)
1210 shutil.chown(filename, user=uid)
1211 check_chown(filename, uid)
1212 shutil.chown(filename, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001213 check_chown(filename, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001214
1215 shutil.chown(dirname, uid, gid)
1216 check_chown(dirname, uid, gid)
1217 shutil.chown(dirname, uid)
1218 check_chown(dirname, uid)
1219 shutil.chown(dirname, user=uid)
1220 check_chown(dirname, uid)
1221 shutil.chown(dirname, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001222 check_chown(dirname, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001223
1224 user = pwd.getpwuid(uid)[0]
1225 group = grp.getgrgid(gid)[0]
1226 shutil.chown(filename, user, group)
1227 check_chown(filename, uid, gid)
1228 shutil.chown(dirname, user, group)
1229 check_chown(dirname, uid, gid)
1230
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001231 def test_copy_return_value(self):
1232 # copy and copy2 both return their destination path.
1233 for fn in (shutil.copy, shutil.copy2):
1234 src_dir = self.mkdtemp()
1235 dst_dir = self.mkdtemp()
1236 src = os.path.join(src_dir, 'foo')
1237 write_file(src, 'foo')
1238 rv = fn(src, dst_dir)
1239 self.assertEqual(rv, os.path.join(dst_dir, 'foo'))
1240 rv = fn(src, os.path.join(dst_dir, 'bar'))
1241 self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
1242
1243 def test_copyfile_return_value(self):
1244 # copytree returns its destination path.
1245 src_dir = self.mkdtemp()
1246 dst_dir = self.mkdtemp()
1247 dst_file = os.path.join(dst_dir, 'bar')
1248 src_file = os.path.join(src_dir, 'foo')
1249 write_file(src_file, 'foo')
1250 rv = shutil.copyfile(src_file, dst_file)
1251 self.assertTrue(os.path.exists(rv))
1252 self.assertEqual(read_file(src_file), read_file(dst_file))
1253
1254 def test_copytree_return_value(self):
1255 # copytree returns its destination path.
1256 src_dir = self.mkdtemp()
1257 dst_dir = src_dir + "dest"
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001258 self.addCleanup(shutil.rmtree, dst_dir, True)
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001259 src = os.path.join(src_dir, 'foo')
1260 write_file(src, 'foo')
1261 rv = shutil.copytree(src_dir, dst_dir)
1262 self.assertEqual(['foo'], os.listdir(rv))
1263
Christian Heimes9bd667a2008-01-20 15:14:11 +00001264
Brian Curtinc57a3452012-06-22 16:00:30 -05001265class TestWhich(unittest.TestCase):
1266
1267 def setUp(self):
1268 self.temp_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001269 self.addCleanup(shutil.rmtree, self.temp_dir, True)
Brian Curtinc57a3452012-06-22 16:00:30 -05001270 # Give the temp_file an ".exe" suffix for all.
1271 # It's needed on Windows and not harmful on other platforms.
1272 self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
1273 suffix=".exe")
1274 os.chmod(self.temp_file.name, stat.S_IXUSR)
1275 self.addCleanup(self.temp_file.close)
1276 self.dir, self.file = os.path.split(self.temp_file.name)
1277
1278 def test_basic(self):
1279 # Given an EXE in a directory, it should be returned.
1280 rv = shutil.which(self.file, path=self.dir)
1281 self.assertEqual(rv, self.temp_file.name)
1282
1283 def test_full_path_short_circuit(self):
1284 # When given the fully qualified path to an executable that exists,
1285 # it should be returned.
1286 rv = shutil.which(self.temp_file.name, path=self.temp_dir)
1287 self.assertEqual(self.temp_file.name, rv)
1288
1289 def test_non_matching_mode(self):
1290 # Set the file read-only and ask for writeable files.
1291 os.chmod(self.temp_file.name, stat.S_IREAD)
1292 rv = shutil.which(self.file, path=self.dir, mode=os.W_OK)
1293 self.assertIsNone(rv)
1294
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001295 def test_relative(self):
1296 old_cwd = os.getcwd()
1297 base_dir, tail_dir = os.path.split(self.dir)
1298 os.chdir(base_dir)
1299 try:
1300 rv = shutil.which(self.file, path=tail_dir)
1301 self.assertEqual(rv, os.path.join(tail_dir, self.file))
1302 finally:
1303 os.chdir(old_cwd)
1304
Brian Curtinc57a3452012-06-22 16:00:30 -05001305 def test_nonexistent_file(self):
1306 # Return None when no matching executable file is found on the path.
1307 rv = shutil.which("foo.exe", path=self.dir)
1308 self.assertIsNone(rv)
1309
1310 @unittest.skipUnless(sys.platform == "win32",
1311 "pathext check is Windows-only")
1312 def test_pathext_checking(self):
1313 # Ask for the file without the ".exe" extension, then ensure that
1314 # it gets found properly with the extension.
1315 rv = shutil.which(self.temp_file.name[:-4], path=self.dir)
1316 self.assertEqual(self.temp_file.name, rv)
1317
1318
Christian Heimesada8c3b2008-03-18 18:26:33 +00001319class TestMove(unittest.TestCase):
1320
1321 def setUp(self):
1322 filename = "foo"
1323 self.src_dir = tempfile.mkdtemp()
1324 self.dst_dir = tempfile.mkdtemp()
1325 self.src_file = os.path.join(self.src_dir, filename)
1326 self.dst_file = os.path.join(self.dst_dir, filename)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001327 with open(self.src_file, "wb") as f:
1328 f.write(b"spam")
1329
1330 def tearDown(self):
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001331 for d in (self.src_dir, self.dst_dir):
Christian Heimesada8c3b2008-03-18 18:26:33 +00001332 try:
1333 if d:
1334 shutil.rmtree(d)
1335 except:
1336 pass
1337
1338 def _check_move_file(self, src, dst, real_dst):
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001339 with open(src, "rb") as f:
1340 contents = f.read()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001341 shutil.move(src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001342 with open(real_dst, "rb") as f:
1343 self.assertEqual(contents, f.read())
Christian Heimesada8c3b2008-03-18 18:26:33 +00001344 self.assertFalse(os.path.exists(src))
1345
1346 def _check_move_dir(self, src, dst, real_dst):
1347 contents = sorted(os.listdir(src))
1348 shutil.move(src, dst)
1349 self.assertEqual(contents, sorted(os.listdir(real_dst)))
1350 self.assertFalse(os.path.exists(src))
1351
1352 def test_move_file(self):
1353 # Move a file to another location on the same filesystem.
1354 self._check_move_file(self.src_file, self.dst_file, self.dst_file)
1355
1356 def test_move_file_to_dir(self):
1357 # Move a file inside an existing dir on the same filesystem.
1358 self._check_move_file(self.src_file, self.dst_dir, self.dst_file)
1359
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001360 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001361 def test_move_file_other_fs(self):
1362 # Move a file to an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001363 self.test_move_file()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001364
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001365 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001366 def test_move_file_to_dir_other_fs(self):
1367 # Move a file to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001368 self.test_move_file_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001369
1370 def test_move_dir(self):
1371 # Move a dir to another location on the same filesystem.
1372 dst_dir = tempfile.mktemp()
1373 try:
1374 self._check_move_dir(self.src_dir, dst_dir, dst_dir)
1375 finally:
1376 try:
1377 shutil.rmtree(dst_dir)
1378 except:
1379 pass
1380
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001381 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001382 def test_move_dir_other_fs(self):
1383 # Move a dir to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001384 self.test_move_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001385
1386 def test_move_dir_to_dir(self):
1387 # Move a dir inside an existing dir on the same filesystem.
1388 self._check_move_dir(self.src_dir, self.dst_dir,
1389 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1390
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001391 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001392 def test_move_dir_to_dir_other_fs(self):
1393 # Move a dir inside an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001394 self.test_move_dir_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001395
1396 def test_existing_file_inside_dest_dir(self):
1397 # A file with the same name inside the destination dir already exists.
1398 with open(self.dst_file, "wb"):
1399 pass
1400 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir)
1401
1402 def test_dont_move_dir_in_itself(self):
1403 # Moving a dir inside itself raises an Error.
1404 dst = os.path.join(self.src_dir, "bar")
1405 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst)
1406
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001407 def test_destinsrc_false_negative(self):
1408 os.mkdir(TESTFN)
1409 try:
1410 for src, dst in [('srcdir', 'srcdir/dest')]:
1411 src = os.path.join(TESTFN, src)
1412 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001413 self.assertTrue(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001414 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001415 'dst (%s) is not in src (%s)' % (dst, src))
1416 finally:
1417 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001418
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001419 def test_destinsrc_false_positive(self):
1420 os.mkdir(TESTFN)
1421 try:
1422 for src, dst in [('srcdir', 'src/dest'), ('srcdir', 'srcdir.new')]:
1423 src = os.path.join(TESTFN, src)
1424 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001425 self.assertFalse(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001426 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001427 'dst (%s) is in src (%s)' % (dst, src))
1428 finally:
1429 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimes9bd667a2008-01-20 15:14:11 +00001430
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001431 @support.skip_unless_symlink
1432 @mock_rename
1433 def test_move_file_symlink(self):
1434 dst = os.path.join(self.src_dir, 'bar')
1435 os.symlink(self.src_file, dst)
1436 shutil.move(dst, self.dst_file)
1437 self.assertTrue(os.path.islink(self.dst_file))
1438 self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
1439
1440 @support.skip_unless_symlink
1441 @mock_rename
1442 def test_move_file_symlink_to_dir(self):
1443 filename = "bar"
1444 dst = os.path.join(self.src_dir, filename)
1445 os.symlink(self.src_file, dst)
1446 shutil.move(dst, self.dst_dir)
1447 final_link = os.path.join(self.dst_dir, filename)
1448 self.assertTrue(os.path.islink(final_link))
1449 self.assertTrue(os.path.samefile(self.src_file, final_link))
1450
1451 @support.skip_unless_symlink
1452 @mock_rename
1453 def test_move_dangling_symlink(self):
1454 src = os.path.join(self.src_dir, 'baz')
1455 dst = os.path.join(self.src_dir, 'bar')
1456 os.symlink(src, dst)
1457 dst_link = os.path.join(self.dst_dir, 'quux')
1458 shutil.move(dst, dst_link)
1459 self.assertTrue(os.path.islink(dst_link))
1460 self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
1461
1462 @support.skip_unless_symlink
1463 @mock_rename
1464 def test_move_dir_symlink(self):
1465 src = os.path.join(self.src_dir, 'baz')
1466 dst = os.path.join(self.src_dir, 'bar')
1467 os.mkdir(src)
1468 os.symlink(src, dst)
1469 dst_link = os.path.join(self.dst_dir, 'quux')
1470 shutil.move(dst, dst_link)
1471 self.assertTrue(os.path.islink(dst_link))
1472 self.assertTrue(os.path.samefile(src, dst_link))
1473
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001474 def test_move_return_value(self):
1475 rv = shutil.move(self.src_file, self.dst_dir)
1476 self.assertEqual(rv,
1477 os.path.join(self.dst_dir, os.path.basename(self.src_file)))
1478
1479 def test_move_as_rename_return_value(self):
1480 rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar'))
1481 self.assertEqual(rv, os.path.join(self.dst_dir, 'bar'))
1482
Tarek Ziadé5340db32010-04-19 22:30:51 +00001483
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001484class TestCopyFile(unittest.TestCase):
1485
1486 _delete = False
1487
1488 class Faux(object):
1489 _entered = False
1490 _exited_with = None
1491 _raised = False
1492 def __init__(self, raise_in_exit=False, suppress_at_exit=True):
1493 self._raise_in_exit = raise_in_exit
1494 self._suppress_at_exit = suppress_at_exit
1495 def read(self, *args):
1496 return ''
1497 def __enter__(self):
1498 self._entered = True
1499 def __exit__(self, exc_type, exc_val, exc_tb):
1500 self._exited_with = exc_type, exc_val, exc_tb
1501 if self._raise_in_exit:
1502 self._raised = True
1503 raise IOError("Cannot close")
1504 return self._suppress_at_exit
1505
1506 def tearDown(self):
1507 if self._delete:
1508 del shutil.open
1509
1510 def _set_shutil_open(self, func):
1511 shutil.open = func
1512 self._delete = True
1513
1514 def test_w_source_open_fails(self):
1515 def _open(filename, mode='r'):
1516 if filename == 'srcfile':
1517 raise IOError('Cannot open "srcfile"')
1518 assert 0 # shouldn't reach here.
1519
1520 self._set_shutil_open(_open)
1521
1522 self.assertRaises(IOError, shutil.copyfile, 'srcfile', 'destfile')
1523
1524 def test_w_dest_open_fails(self):
1525
1526 srcfile = self.Faux()
1527
1528 def _open(filename, mode='r'):
1529 if filename == 'srcfile':
1530 return srcfile
1531 if filename == 'destfile':
1532 raise IOError('Cannot open "destfile"')
1533 assert 0 # shouldn't reach here.
1534
1535 self._set_shutil_open(_open)
1536
1537 shutil.copyfile('srcfile', 'destfile')
1538 self.assertTrue(srcfile._entered)
1539 self.assertTrue(srcfile._exited_with[0] is IOError)
1540 self.assertEqual(srcfile._exited_with[1].args,
1541 ('Cannot open "destfile"',))
1542
1543 def test_w_dest_close_fails(self):
1544
1545 srcfile = self.Faux()
1546 destfile = self.Faux(True)
1547
1548 def _open(filename, mode='r'):
1549 if filename == 'srcfile':
1550 return srcfile
1551 if filename == 'destfile':
1552 return destfile
1553 assert 0 # shouldn't reach here.
1554
1555 self._set_shutil_open(_open)
1556
1557 shutil.copyfile('srcfile', 'destfile')
1558 self.assertTrue(srcfile._entered)
1559 self.assertTrue(destfile._entered)
1560 self.assertTrue(destfile._raised)
1561 self.assertTrue(srcfile._exited_with[0] is IOError)
1562 self.assertEqual(srcfile._exited_with[1].args,
1563 ('Cannot close',))
1564
1565 def test_w_source_close_fails(self):
1566
1567 srcfile = self.Faux(True)
1568 destfile = self.Faux()
1569
1570 def _open(filename, mode='r'):
1571 if filename == 'srcfile':
1572 return srcfile
1573 if filename == 'destfile':
1574 return destfile
1575 assert 0 # shouldn't reach here.
1576
1577 self._set_shutil_open(_open)
1578
1579 self.assertRaises(IOError,
1580 shutil.copyfile, 'srcfile', 'destfile')
1581 self.assertTrue(srcfile._entered)
1582 self.assertTrue(destfile._entered)
1583 self.assertFalse(destfile._raised)
1584 self.assertTrue(srcfile._exited_with[0] is None)
1585 self.assertTrue(srcfile._raised)
1586
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001587 def test_move_dir_caseinsensitive(self):
1588 # Renames a folder to the same name
1589 # but a different case.
1590
1591 self.src_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001592 self.addCleanup(shutil.rmtree, self.src_dir, True)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001593 dst_dir = os.path.join(
1594 os.path.dirname(self.src_dir),
1595 os.path.basename(self.src_dir).upper())
1596 self.assertNotEqual(self.src_dir, dst_dir)
1597
1598 try:
1599 shutil.move(self.src_dir, dst_dir)
1600 self.assertTrue(os.path.isdir(dst_dir))
1601 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +02001602 os.rmdir(dst_dir)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001603
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001604class TermsizeTests(unittest.TestCase):
1605 def test_does_not_crash(self):
1606 """Check if get_terminal_size() returns a meaningful value.
1607
1608 There's no easy portable way to actually check the size of the
1609 terminal, so let's check if it returns something sensible instead.
1610 """
1611 size = shutil.get_terminal_size()
Antoine Pitroucfade362012-02-08 23:48:59 +01001612 self.assertGreaterEqual(size.columns, 0)
1613 self.assertGreaterEqual(size.lines, 0)
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001614
1615 def test_os_environ_first(self):
1616 "Check if environment variables have precedence"
1617
1618 with support.EnvironmentVarGuard() as env:
1619 env['COLUMNS'] = '777'
1620 size = shutil.get_terminal_size()
1621 self.assertEqual(size.columns, 777)
1622
1623 with support.EnvironmentVarGuard() as env:
1624 env['LINES'] = '888'
1625 size = shutil.get_terminal_size()
1626 self.assertEqual(size.lines, 888)
1627
1628 @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
1629 def test_stty_match(self):
1630 """Check if stty returns the same results ignoring env
1631
1632 This test will fail if stdin and stdout are connected to
1633 different terminals with different sizes. Nevertheless, such
1634 situations should be pretty rare.
1635 """
1636 try:
1637 size = subprocess.check_output(['stty', 'size']).decode().split()
1638 except (FileNotFoundError, subprocess.CalledProcessError):
1639 self.skipTest("stty invocation failed")
1640 expected = (int(size[1]), int(size[0])) # reversed order
1641
1642 with support.EnvironmentVarGuard() as env:
1643 del env['LINES']
1644 del env['COLUMNS']
1645 actual = shutil.get_terminal_size()
1646
1647 self.assertEqual(expected, actual)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001648
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001649
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001650def test_main():
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001651 support.run_unittest(TestShutil, TestMove, TestCopyFile,
Brian Curtinc57a3452012-06-22 16:00:30 -05001652 TermsizeTests, TestWhich)
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001653
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001654if __name__ == '__main__':
Walter Dörwald21d3a322003-05-01 17:45:56 +00001655 test_main()