blob: 308f4f2b7b1197844e4a7e203e04af0abd6f85ec [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)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100174 # The reason for this rather odd construct is that Windows sprinkles
175 # a \*.* at the end of file names. But only sometimes on some buildbots
176 possible_args = [filename, os.path.join(filename, '*.*')]
177 self.assertIn(cm.exception.filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100178 self.assertTrue(os.path.exists(filename))
Hynek Schlawackb5501102012-12-10 09:11:25 +0100179 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100180 shutil.rmtree(filename, ignore_errors=True)
181 self.assertTrue(os.path.exists(filename))
182 errors = []
183 def onerror(*args):
184 errors.append(args)
185 shutil.rmtree(filename, onerror=onerror)
186 self.assertEqual(len(errors), 2)
187 self.assertIs(errors[0][0], os.listdir)
188 self.assertEqual(errors[0][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100189 self.assertIsInstance(errors[0][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100190 self.assertIn(errors[0][2][1].filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100191 self.assertIs(errors[1][0], os.rmdir)
192 self.assertEqual(errors[1][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100193 self.assertIsInstance(errors[1][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100194 self.assertIn(errors[1][2][1].filename, possible_args)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000195
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000196
Johannes Gijsbersb8b09d02004-12-06 20:50:15 +0000197 # See bug #1071513 for why we don't run this on cygwin
198 # and bug #1076467 for why we don't run this as root.
199 if (hasattr(os, 'chmod') and sys.platform[:6] != 'cygwin'
Johannes Gijsbers6b220b02004-12-12 15:52:57 +0000200 and not (hasattr(os, 'geteuid') and os.geteuid() == 0)):
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000201 def test_on_error(self):
202 self.errorState = 0
203 os.mkdir(TESTFN)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200204 self.addCleanup(shutil.rmtree, TESTFN)
205
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200206 self.child_file_path = os.path.join(TESTFN, 'a')
207 self.child_dir_path = os.path.join(TESTFN, 'b')
208 support.create_empty_file(self.child_file_path)
209 os.mkdir(self.child_dir_path)
Tim Peters4590c002004-11-01 02:40:52 +0000210 old_dir_mode = os.stat(TESTFN).st_mode
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200211 old_child_file_mode = os.stat(self.child_file_path).st_mode
212 old_child_dir_mode = os.stat(self.child_dir_path).st_mode
Tim Peters4590c002004-11-01 02:40:52 +0000213 # Make unwritable.
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200214 new_mode = stat.S_IREAD|stat.S_IEXEC
215 os.chmod(self.child_file_path, new_mode)
216 os.chmod(self.child_dir_path, new_mode)
217 os.chmod(TESTFN, new_mode)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000218
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200219 self.addCleanup(os.chmod, TESTFN, old_dir_mode)
220 self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
221 self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
222
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000223 shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
Johannes Gijsbers8e6f2de2004-11-23 09:27:27 +0000224 # Test whether onerror has actually been called.
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200225 self.assertEqual(self.errorState, 3,
226 "Expected call to onerror function did not "
227 "happen.")
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000228
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000229 def check_args_to_onerror(self, func, arg, exc):
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000230 # test_rmtree_errors deliberately runs rmtree
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200231 # on a directory that is chmod 500, which will fail.
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000232 # This function is run when shutil.rmtree fails.
233 # 99.9% of the time it initially fails to remove
234 # a file in the directory, so the first time through
235 # func is os.remove.
236 # However, some Linux machines running ZFS on
237 # FUSE experienced a failure earlier in the process
238 # at os.listdir. The first failure may legally
239 # be either.
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200240 if self.errorState < 2:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200241 if func is os.unlink:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200242 self.assertEqual(arg, self.child_file_path)
243 elif func is os.rmdir:
244 self.assertEqual(arg, self.child_dir_path)
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000245 else:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200246 self.assertIs(func, os.listdir)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200247 self.assertIn(arg, [TESTFN, self.child_dir_path])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000248 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200249 self.errorState += 1
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000250 else:
251 self.assertEqual(func, os.rmdir)
252 self.assertEqual(arg, TESTFN)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000253 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200254 self.errorState = 3
255
256 def test_rmtree_does_not_choke_on_failing_lstat(self):
257 try:
258 orig_lstat = os.lstat
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200259 def raiser(fn, *args, **kwargs):
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200260 if fn != TESTFN:
261 raise OSError()
262 else:
263 return orig_lstat(fn)
264 os.lstat = raiser
265
266 os.mkdir(TESTFN)
267 write_file((TESTFN, 'foo'), 'foo')
268 shutil.rmtree(TESTFN)
269 finally:
270 os.lstat = orig_lstat
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000271
Antoine Pitrou78091e62011-12-29 18:54:15 +0100272 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
273 @support.skip_unless_symlink
274 def test_copymode_follow_symlinks(self):
275 tmp_dir = self.mkdtemp()
276 src = os.path.join(tmp_dir, 'foo')
277 dst = os.path.join(tmp_dir, 'bar')
278 src_link = os.path.join(tmp_dir, 'baz')
279 dst_link = os.path.join(tmp_dir, 'quux')
280 write_file(src, 'foo')
281 write_file(dst, 'foo')
282 os.symlink(src, src_link)
283 os.symlink(dst, dst_link)
284 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
285 # file to file
286 os.chmod(dst, stat.S_IRWXO)
287 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
288 shutil.copymode(src, dst)
289 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
290 # follow src link
291 os.chmod(dst, stat.S_IRWXO)
292 shutil.copymode(src_link, dst)
293 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
294 # follow dst link
295 os.chmod(dst, stat.S_IRWXO)
296 shutil.copymode(src, dst_link)
297 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
298 # follow both links
299 os.chmod(dst, stat.S_IRWXO)
300 shutil.copymode(src_link, dst)
301 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
302
303 @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
304 @support.skip_unless_symlink
305 def test_copymode_symlink_to_symlink(self):
306 tmp_dir = self.mkdtemp()
307 src = os.path.join(tmp_dir, 'foo')
308 dst = os.path.join(tmp_dir, 'bar')
309 src_link = os.path.join(tmp_dir, 'baz')
310 dst_link = os.path.join(tmp_dir, 'quux')
311 write_file(src, 'foo')
312 write_file(dst, 'foo')
313 os.symlink(src, src_link)
314 os.symlink(dst, dst_link)
315 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
316 os.chmod(dst, stat.S_IRWXU)
317 os.lchmod(src_link, stat.S_IRWXO|stat.S_IRWXG)
318 # link to link
319 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700320 shutil.copymode(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100321 self.assertEqual(os.lstat(src_link).st_mode,
322 os.lstat(dst_link).st_mode)
323 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
324 # src link - use chmod
325 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700326 shutil.copymode(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100327 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
328 # dst link - use chmod
329 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700330 shutil.copymode(src, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100331 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
332
333 @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing')
334 @support.skip_unless_symlink
335 def test_copymode_symlink_to_symlink_wo_lchmod(self):
336 tmp_dir = self.mkdtemp()
337 src = os.path.join(tmp_dir, 'foo')
338 dst = os.path.join(tmp_dir, 'bar')
339 src_link = os.path.join(tmp_dir, 'baz')
340 dst_link = os.path.join(tmp_dir, 'quux')
341 write_file(src, 'foo')
342 write_file(dst, 'foo')
343 os.symlink(src, src_link)
344 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700345 shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail
Antoine Pitrou78091e62011-12-29 18:54:15 +0100346
347 @support.skip_unless_symlink
348 def test_copystat_symlinks(self):
349 tmp_dir = self.mkdtemp()
350 src = os.path.join(tmp_dir, 'foo')
351 dst = os.path.join(tmp_dir, 'bar')
352 src_link = os.path.join(tmp_dir, 'baz')
353 dst_link = os.path.join(tmp_dir, 'qux')
354 write_file(src, 'foo')
355 src_stat = os.stat(src)
356 os.utime(src, (src_stat.st_atime,
357 src_stat.st_mtime - 42.0)) # ensure different mtimes
358 write_file(dst, 'bar')
359 self.assertNotEqual(os.stat(src).st_mtime, os.stat(dst).st_mtime)
360 os.symlink(src, src_link)
361 os.symlink(dst, dst_link)
362 if hasattr(os, 'lchmod'):
363 os.lchmod(src_link, stat.S_IRWXO)
364 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
365 os.lchflags(src_link, stat.UF_NODUMP)
366 src_link_stat = os.lstat(src_link)
367 # follow
368 if hasattr(os, 'lchmod'):
Larry Hastingsb4038062012-07-15 10:57:38 -0700369 shutil.copystat(src_link, dst_link, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100370 self.assertNotEqual(src_link_stat.st_mode, os.stat(dst).st_mode)
371 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700372 shutil.copystat(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100373 dst_link_stat = os.lstat(dst_link)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700374 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100375 for attr in 'st_atime', 'st_mtime':
376 # The modification times may be truncated in the new file.
377 self.assertLessEqual(getattr(src_link_stat, attr),
378 getattr(dst_link_stat, attr) + 1)
379 if hasattr(os, 'lchmod'):
380 self.assertEqual(src_link_stat.st_mode, dst_link_stat.st_mode)
381 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
382 self.assertEqual(src_link_stat.st_flags, dst_link_stat.st_flags)
383 # tell to follow but dst is not a link
Larry Hastingsb4038062012-07-15 10:57:38 -0700384 shutil.copystat(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100385 self.assertTrue(abs(os.stat(src).st_mtime - os.stat(dst).st_mtime) <
386 00000.1)
387
Ned Deilybaf75712012-05-10 17:05:19 -0700388 @unittest.skipUnless(hasattr(os, 'chflags') and
389 hasattr(errno, 'EOPNOTSUPP') and
390 hasattr(errno, 'ENOTSUP'),
391 "requires os.chflags, EOPNOTSUPP & ENOTSUP")
392 def test_copystat_handles_harmless_chflags_errors(self):
393 tmpdir = self.mkdtemp()
394 file1 = os.path.join(tmpdir, 'file1')
395 file2 = os.path.join(tmpdir, 'file2')
396 write_file(file1, 'xxx')
397 write_file(file2, 'xxx')
398
399 def make_chflags_raiser(err):
400 ex = OSError()
401
Larry Hastings90867a52012-06-22 17:01:41 -0700402 def _chflags_raiser(path, flags, *, follow_symlinks=True):
Ned Deilybaf75712012-05-10 17:05:19 -0700403 ex.errno = err
404 raise ex
405 return _chflags_raiser
406 old_chflags = os.chflags
407 try:
408 for err in errno.EOPNOTSUPP, errno.ENOTSUP:
409 os.chflags = make_chflags_raiser(err)
410 shutil.copystat(file1, file2)
411 # assert others errors break it
412 os.chflags = make_chflags_raiser(errno.EOPNOTSUPP + errno.ENOTSUP)
413 self.assertRaises(OSError, shutil.copystat, file1, file2)
414 finally:
415 os.chflags = old_chflags
416
Antoine Pitrou424246f2012-05-12 19:02:01 +0200417 @support.skip_unless_xattr
418 def test_copyxattr(self):
419 tmp_dir = self.mkdtemp()
420 src = os.path.join(tmp_dir, 'foo')
421 write_file(src, 'foo')
422 dst = os.path.join(tmp_dir, 'bar')
423 write_file(dst, 'bar')
424
425 # no xattr == no problem
426 shutil._copyxattr(src, dst)
427 # common case
428 os.setxattr(src, 'user.foo', b'42')
429 os.setxattr(src, 'user.bar', b'43')
430 shutil._copyxattr(src, dst)
431 self.assertEqual(os.listxattr(src), os.listxattr(dst))
432 self.assertEqual(
433 os.getxattr(src, 'user.foo'),
434 os.getxattr(dst, 'user.foo'))
435 # check errors don't affect other attrs
436 os.remove(dst)
437 write_file(dst, 'bar')
438 os_error = OSError(errno.EPERM, 'EPERM')
439
Larry Hastings9cf065c2012-06-22 16:30:09 -0700440 def _raise_on_user_foo(fname, attr, val, **kwargs):
Antoine Pitrou424246f2012-05-12 19:02:01 +0200441 if attr == 'user.foo':
442 raise os_error
443 else:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700444 orig_setxattr(fname, attr, val, **kwargs)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200445 try:
446 orig_setxattr = os.setxattr
447 os.setxattr = _raise_on_user_foo
448 shutil._copyxattr(src, dst)
Antoine Pitrou61597d32012-05-12 23:37:35 +0200449 self.assertIn('user.bar', os.listxattr(dst))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200450 finally:
451 os.setxattr = orig_setxattr
Hynek Schlawack0beab052013-02-05 08:22:44 +0100452 # the source filesystem not supporting xattrs should be ok, too.
453 def _raise_on_src(fname, *, follow_symlinks=True):
454 if fname == src:
455 raise OSError(errno.ENOTSUP, 'Operation not supported')
456 return orig_listxattr(fname, follow_symlinks=follow_symlinks)
457 try:
458 orig_listxattr = os.listxattr
459 os.listxattr = _raise_on_src
460 shutil._copyxattr(src, dst)
461 finally:
462 os.listxattr = orig_listxattr
Antoine Pitrou424246f2012-05-12 19:02:01 +0200463
Larry Hastingsad5ae042012-07-14 17:55:11 -0700464 # test that shutil.copystat copies xattrs
465 src = os.path.join(tmp_dir, 'the_original')
466 write_file(src, src)
467 os.setxattr(src, 'user.the_value', b'fiddly')
468 dst = os.path.join(tmp_dir, 'the_copy')
469 write_file(dst, dst)
470 shutil.copystat(src, dst)
Hynek Schlawackc2d481f2012-07-16 17:11:10 +0200471 self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700472
Antoine Pitrou424246f2012-05-12 19:02:01 +0200473 @support.skip_unless_symlink
474 @support.skip_unless_xattr
475 @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,
476 'root privileges required')
477 def test_copyxattr_symlinks(self):
478 # On Linux, it's only possible to access non-user xattr for symlinks;
479 # which in turn require root privileges. This test should be expanded
480 # as soon as other platforms gain support for extended attributes.
481 tmp_dir = self.mkdtemp()
482 src = os.path.join(tmp_dir, 'foo')
483 src_link = os.path.join(tmp_dir, 'baz')
484 write_file(src, 'foo')
485 os.symlink(src, src_link)
486 os.setxattr(src, 'trusted.foo', b'42')
Larry Hastings9cf065c2012-06-22 16:30:09 -0700487 os.setxattr(src_link, 'trusted.foo', b'43', follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200488 dst = os.path.join(tmp_dir, 'bar')
489 dst_link = os.path.join(tmp_dir, 'qux')
490 write_file(dst, 'bar')
491 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700492 shutil._copyxattr(src_link, dst_link, follow_symlinks=False)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700493 self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43')
Antoine Pitrou424246f2012-05-12 19:02:01 +0200494 self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo')
Larry Hastingsb4038062012-07-15 10:57:38 -0700495 shutil._copyxattr(src_link, dst, follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200496 self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43')
497
Antoine Pitrou78091e62011-12-29 18:54:15 +0100498 @support.skip_unless_symlink
499 def test_copy_symlinks(self):
500 tmp_dir = self.mkdtemp()
501 src = os.path.join(tmp_dir, 'foo')
502 dst = os.path.join(tmp_dir, 'bar')
503 src_link = os.path.join(tmp_dir, 'baz')
504 write_file(src, 'foo')
505 os.symlink(src, src_link)
506 if hasattr(os, 'lchmod'):
507 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
508 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700509 shutil.copy(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100510 self.assertFalse(os.path.islink(dst))
511 self.assertEqual(read_file(src), read_file(dst))
512 os.remove(dst)
513 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700514 shutil.copy(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100515 self.assertTrue(os.path.islink(dst))
516 self.assertEqual(os.readlink(dst), os.readlink(src_link))
517 if hasattr(os, 'lchmod'):
518 self.assertEqual(os.lstat(src_link).st_mode,
519 os.lstat(dst).st_mode)
520
521 @support.skip_unless_symlink
522 def test_copy2_symlinks(self):
523 tmp_dir = self.mkdtemp()
524 src = os.path.join(tmp_dir, 'foo')
525 dst = os.path.join(tmp_dir, 'bar')
526 src_link = os.path.join(tmp_dir, 'baz')
527 write_file(src, 'foo')
528 os.symlink(src, src_link)
529 if hasattr(os, 'lchmod'):
530 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
531 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
532 os.lchflags(src_link, stat.UF_NODUMP)
533 src_stat = os.stat(src)
534 src_link_stat = os.lstat(src_link)
535 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700536 shutil.copy2(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100537 self.assertFalse(os.path.islink(dst))
538 self.assertEqual(read_file(src), read_file(dst))
539 os.remove(dst)
540 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700541 shutil.copy2(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100542 self.assertTrue(os.path.islink(dst))
543 self.assertEqual(os.readlink(dst), os.readlink(src_link))
544 dst_stat = os.lstat(dst)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700545 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100546 for attr in 'st_atime', 'st_mtime':
547 # The modification times may be truncated in the new file.
548 self.assertLessEqual(getattr(src_link_stat, attr),
549 getattr(dst_stat, attr) + 1)
550 if hasattr(os, 'lchmod'):
551 self.assertEqual(src_link_stat.st_mode, dst_stat.st_mode)
552 self.assertNotEqual(src_stat.st_mode, dst_stat.st_mode)
553 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
554 self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags)
555
Antoine Pitrou424246f2012-05-12 19:02:01 +0200556 @support.skip_unless_xattr
557 def test_copy2_xattr(self):
558 tmp_dir = self.mkdtemp()
559 src = os.path.join(tmp_dir, 'foo')
560 dst = os.path.join(tmp_dir, 'bar')
561 write_file(src, 'foo')
562 os.setxattr(src, 'user.foo', b'42')
563 shutil.copy2(src, dst)
564 self.assertEqual(
565 os.getxattr(src, 'user.foo'),
566 os.getxattr(dst, 'user.foo'))
567 os.remove(dst)
568
Antoine Pitrou78091e62011-12-29 18:54:15 +0100569 @support.skip_unless_symlink
570 def test_copyfile_symlinks(self):
571 tmp_dir = self.mkdtemp()
572 src = os.path.join(tmp_dir, 'src')
573 dst = os.path.join(tmp_dir, 'dst')
574 dst_link = os.path.join(tmp_dir, 'dst_link')
575 link = os.path.join(tmp_dir, 'link')
576 write_file(src, 'foo')
577 os.symlink(src, link)
578 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700579 shutil.copyfile(link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100580 self.assertTrue(os.path.islink(dst_link))
581 self.assertEqual(os.readlink(link), os.readlink(dst_link))
582 # follow
583 shutil.copyfile(link, dst)
584 self.assertFalse(os.path.islink(dst))
585
Hynek Schlawack2100b422012-06-23 20:28:32 +0200586 def test_rmtree_uses_safe_fd_version_if_available(self):
Hynek Schlawackd0f6e0a2012-06-29 08:28:20 +0200587 _use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
588 os.supports_dir_fd and
589 os.listdir in os.supports_fd and
590 os.stat in os.supports_follow_symlinks)
591 if _use_fd_functions:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200592 self.assertTrue(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000593 self.assertTrue(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200594 tmp_dir = self.mkdtemp()
595 d = os.path.join(tmp_dir, 'a')
596 os.mkdir(d)
597 try:
598 real_rmtree = shutil._rmtree_safe_fd
599 class Called(Exception): pass
600 def _raiser(*args, **kwargs):
601 raise Called
602 shutil._rmtree_safe_fd = _raiser
603 self.assertRaises(Called, shutil.rmtree, d)
604 finally:
605 shutil._rmtree_safe_fd = real_rmtree
606 else:
607 self.assertFalse(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000608 self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200609
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000610 def test_rmtree_dont_delete_file(self):
611 # When called on a file instead of a directory, don't delete it.
612 handle, path = tempfile.mkstemp()
Victor Stinnerbf816222011-06-30 23:25:47 +0200613 os.close(handle)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200614 self.assertRaises(NotADirectoryError, shutil.rmtree, path)
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000615 os.remove(path)
616
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000617 def test_copytree_simple(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000618 src_dir = tempfile.mkdtemp()
619 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200620 self.addCleanup(shutil.rmtree, src_dir)
621 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
622 write_file((src_dir, 'test.txt'), '123')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000623 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200624 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000625
Éric Araujoa7e33a12011-08-12 19:51:35 +0200626 shutil.copytree(src_dir, dst_dir)
627 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test.txt')))
628 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'test_dir')))
629 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test_dir',
630 'test.txt')))
631 actual = read_file((dst_dir, 'test.txt'))
632 self.assertEqual(actual, '123')
633 actual = read_file((dst_dir, 'test_dir', 'test.txt'))
634 self.assertEqual(actual, '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000635
Antoine Pitrou78091e62011-12-29 18:54:15 +0100636 @support.skip_unless_symlink
637 def test_copytree_symlinks(self):
638 tmp_dir = self.mkdtemp()
639 src_dir = os.path.join(tmp_dir, 'src')
640 dst_dir = os.path.join(tmp_dir, 'dst')
641 sub_dir = os.path.join(src_dir, 'sub')
642 os.mkdir(src_dir)
643 os.mkdir(sub_dir)
644 write_file((src_dir, 'file.txt'), 'foo')
645 src_link = os.path.join(sub_dir, 'link')
646 dst_link = os.path.join(dst_dir, 'sub/link')
647 os.symlink(os.path.join(src_dir, 'file.txt'),
648 src_link)
649 if hasattr(os, 'lchmod'):
650 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
651 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
652 os.lchflags(src_link, stat.UF_NODUMP)
653 src_stat = os.lstat(src_link)
654 shutil.copytree(src_dir, dst_dir, symlinks=True)
655 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'sub', 'link')))
656 self.assertEqual(os.readlink(os.path.join(dst_dir, 'sub', 'link')),
657 os.path.join(src_dir, 'file.txt'))
658 dst_stat = os.lstat(dst_link)
659 if hasattr(os, 'lchmod'):
660 self.assertEqual(dst_stat.st_mode, src_stat.st_mode)
661 if hasattr(os, 'lchflags'):
662 self.assertEqual(dst_stat.st_flags, src_stat.st_flags)
663
Georg Brandl2ee470f2008-07-16 12:55:28 +0000664 def test_copytree_with_exclude(self):
Georg Brandl2ee470f2008-07-16 12:55:28 +0000665 # creating data
666 join = os.path.join
667 exists = os.path.exists
668 src_dir = tempfile.mkdtemp()
Georg Brandl2ee470f2008-07-16 12:55:28 +0000669 try:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000670 dst_dir = join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200671 write_file((src_dir, 'test.txt'), '123')
672 write_file((src_dir, 'test.tmp'), '123')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000673 os.mkdir(join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200674 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000675 os.mkdir(join(src_dir, 'test_dir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200676 write_file((src_dir, 'test_dir2', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000677 os.mkdir(join(src_dir, 'test_dir2', 'subdir'))
678 os.mkdir(join(src_dir, 'test_dir2', 'subdir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200679 write_file((src_dir, 'test_dir2', 'subdir', 'test.txt'), '456')
680 write_file((src_dir, 'test_dir2', 'subdir2', 'test.py'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000681
682 # testing glob-like patterns
683 try:
684 patterns = shutil.ignore_patterns('*.tmp', 'test_dir2')
685 shutil.copytree(src_dir, dst_dir, ignore=patterns)
686 # checking the result: some elements should not be copied
687 self.assertTrue(exists(join(dst_dir, 'test.txt')))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200688 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
689 self.assertFalse(exists(join(dst_dir, 'test_dir2')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000690 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200691 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000692 try:
693 patterns = shutil.ignore_patterns('*.tmp', 'subdir*')
694 shutil.copytree(src_dir, dst_dir, ignore=patterns)
695 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200696 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
697 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2')))
698 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000699 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200700 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000701
702 # testing callable-style
703 try:
704 def _filter(src, names):
705 res = []
706 for name in names:
707 path = os.path.join(src, name)
708
709 if (os.path.isdir(path) and
710 path.split()[-1] == 'subdir'):
711 res.append(name)
712 elif os.path.splitext(path)[-1] in ('.py'):
713 res.append(name)
714 return res
715
716 shutil.copytree(src_dir, dst_dir, ignore=_filter)
717
718 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200719 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2',
720 'test.py')))
721 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000722
723 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200724 shutil.rmtree(dst_dir)
Georg Brandl2ee470f2008-07-16 12:55:28 +0000725 finally:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000726 shutil.rmtree(src_dir)
727 shutil.rmtree(os.path.dirname(dst_dir))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000728
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000729 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000730 def test_dont_copy_file_onto_link_to_itself(self):
Georg Brandl724d0892010-12-05 07:51:39 +0000731 # Temporarily disable test on Windows.
732 if os.name == 'nt':
733 return
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000734 # bug 851123.
735 os.mkdir(TESTFN)
736 src = os.path.join(TESTFN, 'cheese')
737 dst = os.path.join(TESTFN, 'shop')
738 try:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000739 with open(src, 'w') as f:
740 f.write('cheddar')
741 os.link(src, dst)
Hynek Schlawack26fe37d2012-07-19 21:41:02 +0200742 self.assertRaises(shutil.Error, shutil.copyfile, src, dst)
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000743 with open(src, 'r') as f:
744 self.assertEqual(f.read(), 'cheddar')
745 os.remove(dst)
746 finally:
747 shutil.rmtree(TESTFN, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000748
Brian Curtin3b4499c2010-12-28 14:31:47 +0000749 @support.skip_unless_symlink
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000750 def test_dont_copy_file_onto_symlink_to_itself(self):
751 # bug 851123.
752 os.mkdir(TESTFN)
753 src = os.path.join(TESTFN, 'cheese')
754 dst = os.path.join(TESTFN, 'shop')
755 try:
756 with open(src, 'w') as f:
757 f.write('cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000758 # Using `src` here would mean we end up with a symlink pointing
759 # to TESTFN/TESTFN/cheese, while it should point at
760 # TESTFN/cheese.
761 os.symlink('cheese', dst)
Hynek Schlawack26fe37d2012-07-19 21:41:02 +0200762 self.assertRaises(shutil.Error, shutil.copyfile, src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000763 with open(src, 'r') as f:
764 self.assertEqual(f.read(), 'cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000765 os.remove(dst)
766 finally:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000767 shutil.rmtree(TESTFN, ignore_errors=True)
Johannes Gijsbers68128712004-08-14 13:57:08 +0000768
Brian Curtin3b4499c2010-12-28 14:31:47 +0000769 @support.skip_unless_symlink
Brian Curtind40e6f72010-07-08 21:39:08 +0000770 def test_rmtree_on_symlink(self):
771 # bug 1669.
772 os.mkdir(TESTFN)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000773 try:
Brian Curtind40e6f72010-07-08 21:39:08 +0000774 src = os.path.join(TESTFN, 'cheese')
775 dst = os.path.join(TESTFN, 'shop')
776 os.mkdir(src)
777 os.symlink(src, dst)
778 self.assertRaises(OSError, shutil.rmtree, dst)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200779 shutil.rmtree(dst, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000780 finally:
Brian Curtind40e6f72010-07-08 21:39:08 +0000781 shutil.rmtree(TESTFN, ignore_errors=True)
782
783 if hasattr(os, "mkfifo"):
784 # Issue #3002: copyfile and copytree block indefinitely on named pipes
785 def test_copyfile_named_pipe(self):
786 os.mkfifo(TESTFN)
787 try:
788 self.assertRaises(shutil.SpecialFileError,
789 shutil.copyfile, TESTFN, TESTFN2)
790 self.assertRaises(shutil.SpecialFileError,
791 shutil.copyfile, __file__, TESTFN)
792 finally:
793 os.remove(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000794
Brian Curtin3b4499c2010-12-28 14:31:47 +0000795 @support.skip_unless_symlink
Brian Curtin52173d42010-12-02 18:29:18 +0000796 def test_copytree_named_pipe(self):
797 os.mkdir(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000798 try:
Brian Curtin52173d42010-12-02 18:29:18 +0000799 subdir = os.path.join(TESTFN, "subdir")
800 os.mkdir(subdir)
801 pipe = os.path.join(subdir, "mypipe")
802 os.mkfifo(pipe)
803 try:
804 shutil.copytree(TESTFN, TESTFN2)
805 except shutil.Error as e:
806 errors = e.args[0]
807 self.assertEqual(len(errors), 1)
808 src, dst, error_msg = errors[0]
809 self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
810 else:
811 self.fail("shutil.Error should have been raised")
812 finally:
813 shutil.rmtree(TESTFN, ignore_errors=True)
814 shutil.rmtree(TESTFN2, ignore_errors=True)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000815
Tarek Ziadé5340db32010-04-19 22:30:51 +0000816 def test_copytree_special_func(self):
817
818 src_dir = self.mkdtemp()
819 dst_dir = os.path.join(self.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200820 write_file((src_dir, 'test.txt'), '123')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000821 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200822 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000823
824 copied = []
825 def _copy(src, dst):
826 copied.append((src, dst))
827
828 shutil.copytree(src_dir, dst_dir, copy_function=_copy)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000829 self.assertEqual(len(copied), 2)
Tarek Ziadé5340db32010-04-19 22:30:51 +0000830
Brian Curtin3b4499c2010-12-28 14:31:47 +0000831 @support.skip_unless_symlink
Tarek Ziadéfb437512010-04-20 08:57:33 +0000832 def test_copytree_dangling_symlinks(self):
833
834 # a dangling symlink raises an error at the end
835 src_dir = self.mkdtemp()
836 dst_dir = os.path.join(self.mkdtemp(), 'destination')
837 os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt'))
838 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200839 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadéfb437512010-04-20 08:57:33 +0000840 self.assertRaises(Error, shutil.copytree, src_dir, dst_dir)
841
842 # a dangling symlink is ignored with the proper flag
843 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
844 shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)
845 self.assertNotIn('test.txt', os.listdir(dst_dir))
846
847 # a dangling symlink is copied if symlinks=True
848 dst_dir = os.path.join(self.mkdtemp(), 'destination3')
849 shutil.copytree(src_dir, dst_dir, symlinks=True)
850 self.assertIn('test.txt', os.listdir(dst_dir))
851
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400852 def _copy_file(self, method):
853 fname = 'test.txt'
854 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200855 write_file((tmpdir, fname), 'xxx')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400856 file1 = os.path.join(tmpdir, fname)
857 tmpdir2 = self.mkdtemp()
858 method(file1, tmpdir2)
859 file2 = os.path.join(tmpdir2, fname)
860 return (file1, file2)
861
862 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
863 def test_copy(self):
864 # Ensure that the copied file exists and has the same mode bits.
865 file1, file2 = self._copy_file(shutil.copy)
866 self.assertTrue(os.path.exists(file2))
867 self.assertEqual(os.stat(file1).st_mode, os.stat(file2).st_mode)
868
869 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
Senthil Kumaran0c2dba52011-07-03 18:21:38 -0700870 @unittest.skipUnless(hasattr(os, 'utime'), 'requires os.utime')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400871 def test_copy2(self):
872 # Ensure that the copied file exists and has the same mode and
873 # modification time bits.
874 file1, file2 = self._copy_file(shutil.copy2)
875 self.assertTrue(os.path.exists(file2))
876 file1_stat = os.stat(file1)
877 file2_stat = os.stat(file2)
878 self.assertEqual(file1_stat.st_mode, file2_stat.st_mode)
879 for attr in 'st_atime', 'st_mtime':
880 # The modification times may be truncated in the new file.
881 self.assertLessEqual(getattr(file1_stat, attr),
882 getattr(file2_stat, attr) + 1)
883 if hasattr(os, 'chflags') and hasattr(file1_stat, 'st_flags'):
884 self.assertEqual(getattr(file1_stat, 'st_flags'),
885 getattr(file2_stat, 'st_flags'))
886
Ezio Melotti975077a2011-05-19 22:03:22 +0300887 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000888 def test_make_tarball(self):
889 # creating something to tar
890 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200891 write_file((tmpdir, 'file1'), 'xxx')
892 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000893 os.mkdir(os.path.join(tmpdir, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200894 write_file((tmpdir, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000895
896 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400897 # force shutil to create the directory
898 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000899 unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
900 "source and target should be on same drive")
901
902 base_name = os.path.join(tmpdir2, 'archive')
903
904 # working with relative paths to avoid tar warnings
905 old_dir = os.getcwd()
906 os.chdir(tmpdir)
907 try:
908 _make_tarball(splitdrive(base_name)[1], '.')
909 finally:
910 os.chdir(old_dir)
911
912 # check if the compressed tarball was created
913 tarball = base_name + '.tar.gz'
914 self.assertTrue(os.path.exists(tarball))
915
916 # trying an uncompressed one
917 base_name = os.path.join(tmpdir2, 'archive')
918 old_dir = os.getcwd()
919 os.chdir(tmpdir)
920 try:
921 _make_tarball(splitdrive(base_name)[1], '.', compress=None)
922 finally:
923 os.chdir(old_dir)
924 tarball = base_name + '.tar'
925 self.assertTrue(os.path.exists(tarball))
926
927 def _tarinfo(self, path):
928 tar = tarfile.open(path)
929 try:
930 names = tar.getnames()
931 names.sort()
932 return tuple(names)
933 finally:
934 tar.close()
935
936 def _create_files(self):
937 # creating something to tar
938 tmpdir = self.mkdtemp()
939 dist = os.path.join(tmpdir, 'dist')
940 os.mkdir(dist)
Éric Araujoa7e33a12011-08-12 19:51:35 +0200941 write_file((dist, 'file1'), 'xxx')
942 write_file((dist, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000943 os.mkdir(os.path.join(dist, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200944 write_file((dist, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000945 os.mkdir(os.path.join(dist, 'sub2'))
946 tmpdir2 = self.mkdtemp()
947 base_name = os.path.join(tmpdir2, 'archive')
948 return tmpdir, tmpdir2, base_name
949
Ezio Melotti975077a2011-05-19 22:03:22 +0300950 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000951 @unittest.skipUnless(find_executable('tar') and find_executable('gzip'),
952 'Need the tar command to run')
953 def test_tarfile_vs_tar(self):
954 tmpdir, tmpdir2, base_name = self._create_files()
955 old_dir = os.getcwd()
956 os.chdir(tmpdir)
957 try:
958 _make_tarball(base_name, 'dist')
959 finally:
960 os.chdir(old_dir)
961
962 # check if the compressed tarball was created
963 tarball = base_name + '.tar.gz'
964 self.assertTrue(os.path.exists(tarball))
965
966 # now create another tarball using `tar`
967 tarball2 = os.path.join(tmpdir, 'archive2.tar.gz')
968 tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist']
969 gzip_cmd = ['gzip', '-f9', 'archive2.tar']
970 old_dir = os.getcwd()
971 os.chdir(tmpdir)
972 try:
973 with captured_stdout() as s:
974 spawn(tar_cmd)
975 spawn(gzip_cmd)
976 finally:
977 os.chdir(old_dir)
978
979 self.assertTrue(os.path.exists(tarball2))
980 # let's compare both tarballs
Ezio Melottib3aedd42010-11-20 19:04:17 +0000981 self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +0000982
983 # trying an uncompressed one
984 base_name = os.path.join(tmpdir2, 'archive')
985 old_dir = os.getcwd()
986 os.chdir(tmpdir)
987 try:
988 _make_tarball(base_name, 'dist', compress=None)
989 finally:
990 os.chdir(old_dir)
991 tarball = base_name + '.tar'
992 self.assertTrue(os.path.exists(tarball))
993
994 # now for a dry_run
995 base_name = os.path.join(tmpdir2, 'archive')
996 old_dir = os.getcwd()
997 os.chdir(tmpdir)
998 try:
999 _make_tarball(base_name, 'dist', compress=None, dry_run=True)
1000 finally:
1001 os.chdir(old_dir)
1002 tarball = base_name + '.tar'
1003 self.assertTrue(os.path.exists(tarball))
1004
Ezio Melotti975077a2011-05-19 22:03:22 +03001005 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001006 @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
1007 def test_make_zipfile(self):
1008 # creating something to tar
1009 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +02001010 write_file((tmpdir, 'file1'), 'xxx')
1011 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001012
1013 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001014 # force shutil to create the directory
1015 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001016 base_name = os.path.join(tmpdir2, 'archive')
1017 _make_zipfile(base_name, tmpdir)
1018
1019 # check if the compressed tarball was created
1020 tarball = base_name + '.zip'
Éric Araujo1c505492010-11-06 02:12:51 +00001021 self.assertTrue(os.path.exists(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001022
1023
1024 def test_make_archive(self):
1025 tmpdir = self.mkdtemp()
1026 base_name = os.path.join(tmpdir, 'archive')
1027 self.assertRaises(ValueError, make_archive, base_name, 'xxx')
1028
Ezio Melotti975077a2011-05-19 22:03:22 +03001029 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001030 def test_make_archive_owner_group(self):
1031 # testing make_archive with owner and group, with various combinations
1032 # this works even if there's not gid/uid support
1033 if UID_GID_SUPPORT:
1034 group = grp.getgrgid(0)[0]
1035 owner = pwd.getpwuid(0)[0]
1036 else:
1037 group = owner = 'root'
1038
1039 base_dir, root_dir, base_name = self._create_files()
1040 base_name = os.path.join(self.mkdtemp() , 'archive')
1041 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
1042 group=group)
1043 self.assertTrue(os.path.exists(res))
1044
1045 res = make_archive(base_name, 'zip', root_dir, base_dir)
1046 self.assertTrue(os.path.exists(res))
1047
1048 res = make_archive(base_name, 'tar', root_dir, base_dir,
1049 owner=owner, group=group)
1050 self.assertTrue(os.path.exists(res))
1051
1052 res = make_archive(base_name, 'tar', root_dir, base_dir,
1053 owner='kjhkjhkjg', group='oihohoh')
1054 self.assertTrue(os.path.exists(res))
1055
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001056
Ezio Melotti975077a2011-05-19 22:03:22 +03001057 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001058 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1059 def test_tarfile_root_owner(self):
1060 tmpdir, tmpdir2, base_name = self._create_files()
1061 old_dir = os.getcwd()
1062 os.chdir(tmpdir)
1063 group = grp.getgrgid(0)[0]
1064 owner = pwd.getpwuid(0)[0]
1065 try:
1066 archive_name = _make_tarball(base_name, 'dist', compress=None,
1067 owner=owner, group=group)
1068 finally:
1069 os.chdir(old_dir)
1070
1071 # check if the compressed tarball was created
1072 self.assertTrue(os.path.exists(archive_name))
1073
1074 # now checks the rights
1075 archive = tarfile.open(archive_name)
1076 try:
1077 for member in archive.getmembers():
Ezio Melottib3aedd42010-11-20 19:04:17 +00001078 self.assertEqual(member.uid, 0)
1079 self.assertEqual(member.gid, 0)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001080 finally:
1081 archive.close()
1082
1083 def test_make_archive_cwd(self):
1084 current_dir = os.getcwd()
1085 def _breaks(*args, **kw):
1086 raise RuntimeError()
1087
1088 register_archive_format('xxx', _breaks, [], 'xxx file')
1089 try:
1090 try:
1091 make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
1092 except Exception:
1093 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +00001094 self.assertEqual(os.getcwd(), current_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001095 finally:
1096 unregister_archive_format('xxx')
1097
1098 def test_register_archive_format(self):
1099
1100 self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
1101 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1102 1)
1103 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1104 [(1, 2), (1, 2, 3)])
1105
1106 register_archive_format('xxx', lambda: x, [(1, 2)], 'xxx file')
1107 formats = [name for name, params in get_archive_formats()]
1108 self.assertIn('xxx', formats)
1109
1110 unregister_archive_format('xxx')
1111 formats = [name for name, params in get_archive_formats()]
1112 self.assertNotIn('xxx', formats)
1113
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001114 def _compare_dirs(self, dir1, dir2):
1115 # check that dir1 and dir2 are equivalent,
1116 # return the diff
1117 diff = []
1118 for root, dirs, files in os.walk(dir1):
1119 for file_ in files:
1120 path = os.path.join(root, file_)
1121 target_path = os.path.join(dir2, os.path.split(path)[-1])
1122 if not os.path.exists(target_path):
1123 diff.append(file_)
1124 return diff
1125
Ezio Melotti975077a2011-05-19 22:03:22 +03001126 @requires_zlib
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001127 def test_unpack_archive(self):
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001128 formats = ['tar', 'gztar', 'zip']
1129 if BZ2_SUPPORTED:
1130 formats.append('bztar')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001131
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001132 for format in formats:
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001133 tmpdir = self.mkdtemp()
1134 base_dir, root_dir, base_name = self._create_files()
1135 tmpdir2 = self.mkdtemp()
1136 filename = make_archive(base_name, format, root_dir, base_dir)
1137
1138 # let's try to unpack it now
1139 unpack_archive(filename, tmpdir2)
1140 diff = self._compare_dirs(tmpdir, tmpdir2)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001141 self.assertEqual(diff, [])
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001142
Nick Coghlanabf202d2011-03-16 13:52:20 -04001143 # and again, this time with the format specified
1144 tmpdir3 = self.mkdtemp()
1145 unpack_archive(filename, tmpdir3, format=format)
1146 diff = self._compare_dirs(tmpdir, tmpdir3)
1147 self.assertEqual(diff, [])
1148 self.assertRaises(shutil.ReadError, unpack_archive, TESTFN)
1149 self.assertRaises(ValueError, unpack_archive, TESTFN, format='xxx')
1150
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001151 def test_unpack_registery(self):
1152
1153 formats = get_unpack_formats()
1154
1155 def _boo(filename, extract_dir, extra):
Ezio Melottib3aedd42010-11-20 19:04:17 +00001156 self.assertEqual(extra, 1)
1157 self.assertEqual(filename, 'stuff.boo')
1158 self.assertEqual(extract_dir, 'xx')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001159
1160 register_unpack_format('Boo', ['.boo', '.b2'], _boo, [('extra', 1)])
1161 unpack_archive('stuff.boo', 'xx')
1162
1163 # trying to register a .boo unpacker again
1164 self.assertRaises(RegistryError, register_unpack_format, 'Boo2',
1165 ['.boo'], _boo)
1166
1167 # should work now
1168 unregister_unpack_format('Boo')
1169 register_unpack_format('Boo2', ['.boo'], _boo)
1170 self.assertIn(('Boo2', ['.boo'], ''), get_unpack_formats())
1171 self.assertNotIn(('Boo', ['.boo'], ''), get_unpack_formats())
1172
1173 # let's leave a clean state
1174 unregister_unpack_format('Boo2')
Ezio Melottib3aedd42010-11-20 19:04:17 +00001175 self.assertEqual(get_unpack_formats(), formats)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001176
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001177 @unittest.skipUnless(hasattr(shutil, 'disk_usage'),
1178 "disk_usage not available on this platform")
1179 def test_disk_usage(self):
1180 usage = shutil.disk_usage(os.getcwd())
Éric Araujo2ee61882011-07-02 16:45:45 +02001181 self.assertGreater(usage.total, 0)
1182 self.assertGreater(usage.used, 0)
1183 self.assertGreaterEqual(usage.free, 0)
1184 self.assertGreaterEqual(usage.total, usage.used)
1185 self.assertGreater(usage.total, usage.free)
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001186
Sandro Tosid902a142011-08-22 23:28:27 +02001187 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1188 @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
1189 def test_chown(self):
1190
1191 # cleaned-up automatically by TestShutil.tearDown method
1192 dirname = self.mkdtemp()
1193 filename = tempfile.mktemp(dir=dirname)
1194 write_file(filename, 'testing chown function')
1195
1196 with self.assertRaises(ValueError):
1197 shutil.chown(filename)
1198
1199 with self.assertRaises(LookupError):
1200 shutil.chown(filename, user='non-exising username')
1201
1202 with self.assertRaises(LookupError):
1203 shutil.chown(filename, group='non-exising groupname')
1204
1205 with self.assertRaises(TypeError):
1206 shutil.chown(filename, b'spam')
1207
1208 with self.assertRaises(TypeError):
1209 shutil.chown(filename, 3.14)
1210
1211 uid = os.getuid()
1212 gid = os.getgid()
1213
1214 def check_chown(path, uid=None, gid=None):
1215 s = os.stat(filename)
1216 if uid is not None:
1217 self.assertEqual(uid, s.st_uid)
1218 if gid is not None:
1219 self.assertEqual(gid, s.st_gid)
1220
1221 shutil.chown(filename, uid, gid)
1222 check_chown(filename, uid, gid)
1223 shutil.chown(filename, uid)
1224 check_chown(filename, uid)
1225 shutil.chown(filename, user=uid)
1226 check_chown(filename, uid)
1227 shutil.chown(filename, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001228 check_chown(filename, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001229
1230 shutil.chown(dirname, uid, gid)
1231 check_chown(dirname, uid, gid)
1232 shutil.chown(dirname, uid)
1233 check_chown(dirname, uid)
1234 shutil.chown(dirname, user=uid)
1235 check_chown(dirname, uid)
1236 shutil.chown(dirname, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001237 check_chown(dirname, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001238
1239 user = pwd.getpwuid(uid)[0]
1240 group = grp.getgrgid(gid)[0]
1241 shutil.chown(filename, user, group)
1242 check_chown(filename, uid, gid)
1243 shutil.chown(dirname, user, group)
1244 check_chown(dirname, uid, gid)
1245
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001246 def test_copy_return_value(self):
1247 # copy and copy2 both return their destination path.
1248 for fn in (shutil.copy, shutil.copy2):
1249 src_dir = self.mkdtemp()
1250 dst_dir = self.mkdtemp()
1251 src = os.path.join(src_dir, 'foo')
1252 write_file(src, 'foo')
1253 rv = fn(src, dst_dir)
1254 self.assertEqual(rv, os.path.join(dst_dir, 'foo'))
1255 rv = fn(src, os.path.join(dst_dir, 'bar'))
1256 self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
1257
1258 def test_copyfile_return_value(self):
1259 # copytree returns its destination path.
1260 src_dir = self.mkdtemp()
1261 dst_dir = self.mkdtemp()
1262 dst_file = os.path.join(dst_dir, 'bar')
1263 src_file = os.path.join(src_dir, 'foo')
1264 write_file(src_file, 'foo')
1265 rv = shutil.copyfile(src_file, dst_file)
1266 self.assertTrue(os.path.exists(rv))
1267 self.assertEqual(read_file(src_file), read_file(dst_file))
1268
1269 def test_copytree_return_value(self):
1270 # copytree returns its destination path.
1271 src_dir = self.mkdtemp()
1272 dst_dir = src_dir + "dest"
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001273 self.addCleanup(shutil.rmtree, dst_dir, True)
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001274 src = os.path.join(src_dir, 'foo')
1275 write_file(src, 'foo')
1276 rv = shutil.copytree(src_dir, dst_dir)
1277 self.assertEqual(['foo'], os.listdir(rv))
1278
Christian Heimes9bd667a2008-01-20 15:14:11 +00001279
Brian Curtinc57a3452012-06-22 16:00:30 -05001280class TestWhich(unittest.TestCase):
1281
1282 def setUp(self):
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001283 self.temp_dir = tempfile.mkdtemp(prefix="Tmp")
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001284 self.addCleanup(shutil.rmtree, self.temp_dir, True)
Brian Curtinc57a3452012-06-22 16:00:30 -05001285 # Give the temp_file an ".exe" suffix for all.
1286 # It's needed on Windows and not harmful on other platforms.
1287 self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001288 prefix="Tmp",
1289 suffix=".Exe")
Brian Curtinc57a3452012-06-22 16:00:30 -05001290 os.chmod(self.temp_file.name, stat.S_IXUSR)
1291 self.addCleanup(self.temp_file.close)
1292 self.dir, self.file = os.path.split(self.temp_file.name)
1293
1294 def test_basic(self):
1295 # Given an EXE in a directory, it should be returned.
1296 rv = shutil.which(self.file, path=self.dir)
1297 self.assertEqual(rv, self.temp_file.name)
1298
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001299 def test_absolute_cmd(self):
Brian Curtinc57a3452012-06-22 16:00:30 -05001300 # When given the fully qualified path to an executable that exists,
1301 # it should be returned.
1302 rv = shutil.which(self.temp_file.name, path=self.temp_dir)
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001303 self.assertEqual(rv, self.temp_file.name)
1304
1305 def test_relative_cmd(self):
1306 # When given the relative path with a directory part to an executable
1307 # that exists, it should be returned.
1308 base_dir, tail_dir = os.path.split(self.dir)
1309 relpath = os.path.join(tail_dir, self.file)
1310 with support.temp_cwd(path=base_dir):
1311 rv = shutil.which(relpath, path=self.temp_dir)
1312 self.assertEqual(rv, relpath)
1313 # But it shouldn't be searched in PATH directories (issue #16957).
1314 with support.temp_cwd(path=self.dir):
1315 rv = shutil.which(relpath, path=base_dir)
1316 self.assertIsNone(rv)
1317
1318 def test_cwd(self):
1319 # Issue #16957
1320 base_dir = os.path.dirname(self.dir)
1321 with support.temp_cwd(path=self.dir):
1322 rv = shutil.which(self.file, path=base_dir)
1323 if sys.platform == "win32":
1324 # Windows: current directory implicitly on PATH
1325 self.assertEqual(rv, os.path.join(os.curdir, self.file))
1326 else:
1327 # Other platforms: shouldn't match in the current directory.
1328 self.assertIsNone(rv)
Brian Curtinc57a3452012-06-22 16:00:30 -05001329
1330 def test_non_matching_mode(self):
1331 # Set the file read-only and ask for writeable files.
1332 os.chmod(self.temp_file.name, stat.S_IREAD)
1333 rv = shutil.which(self.file, path=self.dir, mode=os.W_OK)
1334 self.assertIsNone(rv)
1335
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001336 def test_relative_path(self):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001337 base_dir, tail_dir = os.path.split(self.dir)
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001338 with support.temp_cwd(path=base_dir):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001339 rv = shutil.which(self.file, path=tail_dir)
1340 self.assertEqual(rv, os.path.join(tail_dir, self.file))
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001341
Brian Curtinc57a3452012-06-22 16:00:30 -05001342 def test_nonexistent_file(self):
1343 # Return None when no matching executable file is found on the path.
1344 rv = shutil.which("foo.exe", path=self.dir)
1345 self.assertIsNone(rv)
1346
1347 @unittest.skipUnless(sys.platform == "win32",
1348 "pathext check is Windows-only")
1349 def test_pathext_checking(self):
1350 # Ask for the file without the ".exe" extension, then ensure that
1351 # it gets found properly with the extension.
Serhiy Storchakad70127a2013-01-24 20:03:49 +02001352 rv = shutil.which(self.file[:-4], path=self.dir)
Serhiy Storchaka80c88f42013-01-22 10:31:36 +02001353 self.assertEqual(rv, self.temp_file.name[:-4] + ".EXE")
Brian Curtinc57a3452012-06-22 16:00:30 -05001354
Barry Warsaw618738b2013-04-16 11:05:03 -04001355 def test_environ_path(self):
1356 with support.EnvironmentVarGuard() as env:
1357 env['PATH'] = self.dir
1358 rv = shutil.which(self.file)
1359 self.assertEqual(rv, self.temp_file.name)
1360
1361 def test_empty_path(self):
1362 base_dir = os.path.dirname(self.dir)
1363 with support.temp_cwd(path=self.dir), \
1364 support.EnvironmentVarGuard() as env:
1365 env['PATH'] = self.dir
1366 rv = shutil.which(self.file, path='')
1367 self.assertIsNone(rv)
1368
1369 def test_empty_path_no_PATH(self):
1370 with support.EnvironmentVarGuard() as env:
1371 env.pop('PATH', None)
1372 rv = shutil.which(self.file)
1373 self.assertIsNone(rv)
1374
Brian Curtinc57a3452012-06-22 16:00:30 -05001375
Christian Heimesada8c3b2008-03-18 18:26:33 +00001376class TestMove(unittest.TestCase):
1377
1378 def setUp(self):
1379 filename = "foo"
1380 self.src_dir = tempfile.mkdtemp()
1381 self.dst_dir = tempfile.mkdtemp()
1382 self.src_file = os.path.join(self.src_dir, filename)
1383 self.dst_file = os.path.join(self.dst_dir, filename)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001384 with open(self.src_file, "wb") as f:
1385 f.write(b"spam")
1386
1387 def tearDown(self):
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001388 for d in (self.src_dir, self.dst_dir):
Christian Heimesada8c3b2008-03-18 18:26:33 +00001389 try:
1390 if d:
1391 shutil.rmtree(d)
1392 except:
1393 pass
1394
1395 def _check_move_file(self, src, dst, real_dst):
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001396 with open(src, "rb") as f:
1397 contents = f.read()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001398 shutil.move(src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001399 with open(real_dst, "rb") as f:
1400 self.assertEqual(contents, f.read())
Christian Heimesada8c3b2008-03-18 18:26:33 +00001401 self.assertFalse(os.path.exists(src))
1402
1403 def _check_move_dir(self, src, dst, real_dst):
1404 contents = sorted(os.listdir(src))
1405 shutil.move(src, dst)
1406 self.assertEqual(contents, sorted(os.listdir(real_dst)))
1407 self.assertFalse(os.path.exists(src))
1408
1409 def test_move_file(self):
1410 # Move a file to another location on the same filesystem.
1411 self._check_move_file(self.src_file, self.dst_file, self.dst_file)
1412
1413 def test_move_file_to_dir(self):
1414 # Move a file inside an existing dir on the same filesystem.
1415 self._check_move_file(self.src_file, self.dst_dir, self.dst_file)
1416
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001417 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001418 def test_move_file_other_fs(self):
1419 # Move a file to an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001420 self.test_move_file()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001421
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001422 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001423 def test_move_file_to_dir_other_fs(self):
1424 # Move a file to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001425 self.test_move_file_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001426
1427 def test_move_dir(self):
1428 # Move a dir to another location on the same filesystem.
1429 dst_dir = tempfile.mktemp()
1430 try:
1431 self._check_move_dir(self.src_dir, dst_dir, dst_dir)
1432 finally:
1433 try:
1434 shutil.rmtree(dst_dir)
1435 except:
1436 pass
1437
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001438 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001439 def test_move_dir_other_fs(self):
1440 # Move a dir to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001441 self.test_move_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001442
1443 def test_move_dir_to_dir(self):
1444 # Move a dir inside an existing dir on the same filesystem.
1445 self._check_move_dir(self.src_dir, self.dst_dir,
1446 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1447
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001448 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001449 def test_move_dir_to_dir_other_fs(self):
1450 # Move a dir inside an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001451 self.test_move_dir_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001452
1453 def test_existing_file_inside_dest_dir(self):
1454 # A file with the same name inside the destination dir already exists.
1455 with open(self.dst_file, "wb"):
1456 pass
1457 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir)
1458
1459 def test_dont_move_dir_in_itself(self):
1460 # Moving a dir inside itself raises an Error.
1461 dst = os.path.join(self.src_dir, "bar")
1462 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst)
1463
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001464 def test_destinsrc_false_negative(self):
1465 os.mkdir(TESTFN)
1466 try:
1467 for src, dst in [('srcdir', 'srcdir/dest')]:
1468 src = os.path.join(TESTFN, src)
1469 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001470 self.assertTrue(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001471 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001472 'dst (%s) is not in src (%s)' % (dst, src))
1473 finally:
1474 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001475
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001476 def test_destinsrc_false_positive(self):
1477 os.mkdir(TESTFN)
1478 try:
1479 for src, dst in [('srcdir', 'src/dest'), ('srcdir', 'srcdir.new')]:
1480 src = os.path.join(TESTFN, src)
1481 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001482 self.assertFalse(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001483 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001484 'dst (%s) is in src (%s)' % (dst, src))
1485 finally:
1486 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimes9bd667a2008-01-20 15:14:11 +00001487
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001488 @support.skip_unless_symlink
1489 @mock_rename
1490 def test_move_file_symlink(self):
1491 dst = os.path.join(self.src_dir, 'bar')
1492 os.symlink(self.src_file, dst)
1493 shutil.move(dst, self.dst_file)
1494 self.assertTrue(os.path.islink(self.dst_file))
1495 self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
1496
1497 @support.skip_unless_symlink
1498 @mock_rename
1499 def test_move_file_symlink_to_dir(self):
1500 filename = "bar"
1501 dst = os.path.join(self.src_dir, filename)
1502 os.symlink(self.src_file, dst)
1503 shutil.move(dst, self.dst_dir)
1504 final_link = os.path.join(self.dst_dir, filename)
1505 self.assertTrue(os.path.islink(final_link))
1506 self.assertTrue(os.path.samefile(self.src_file, final_link))
1507
1508 @support.skip_unless_symlink
1509 @mock_rename
1510 def test_move_dangling_symlink(self):
1511 src = os.path.join(self.src_dir, 'baz')
1512 dst = os.path.join(self.src_dir, 'bar')
1513 os.symlink(src, dst)
1514 dst_link = os.path.join(self.dst_dir, 'quux')
1515 shutil.move(dst, dst_link)
1516 self.assertTrue(os.path.islink(dst_link))
1517 self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
1518
1519 @support.skip_unless_symlink
1520 @mock_rename
1521 def test_move_dir_symlink(self):
1522 src = os.path.join(self.src_dir, 'baz')
1523 dst = os.path.join(self.src_dir, 'bar')
1524 os.mkdir(src)
1525 os.symlink(src, dst)
1526 dst_link = os.path.join(self.dst_dir, 'quux')
1527 shutil.move(dst, dst_link)
1528 self.assertTrue(os.path.islink(dst_link))
1529 self.assertTrue(os.path.samefile(src, dst_link))
1530
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001531 def test_move_return_value(self):
1532 rv = shutil.move(self.src_file, self.dst_dir)
1533 self.assertEqual(rv,
1534 os.path.join(self.dst_dir, os.path.basename(self.src_file)))
1535
1536 def test_move_as_rename_return_value(self):
1537 rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar'))
1538 self.assertEqual(rv, os.path.join(self.dst_dir, 'bar'))
1539
Tarek Ziadé5340db32010-04-19 22:30:51 +00001540
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001541class TestCopyFile(unittest.TestCase):
1542
1543 _delete = False
1544
1545 class Faux(object):
1546 _entered = False
1547 _exited_with = None
1548 _raised = False
1549 def __init__(self, raise_in_exit=False, suppress_at_exit=True):
1550 self._raise_in_exit = raise_in_exit
1551 self._suppress_at_exit = suppress_at_exit
1552 def read(self, *args):
1553 return ''
1554 def __enter__(self):
1555 self._entered = True
1556 def __exit__(self, exc_type, exc_val, exc_tb):
1557 self._exited_with = exc_type, exc_val, exc_tb
1558 if self._raise_in_exit:
1559 self._raised = True
1560 raise IOError("Cannot close")
1561 return self._suppress_at_exit
1562
1563 def tearDown(self):
1564 if self._delete:
1565 del shutil.open
1566
1567 def _set_shutil_open(self, func):
1568 shutil.open = func
1569 self._delete = True
1570
1571 def test_w_source_open_fails(self):
1572 def _open(filename, mode='r'):
1573 if filename == 'srcfile':
1574 raise IOError('Cannot open "srcfile"')
1575 assert 0 # shouldn't reach here.
1576
1577 self._set_shutil_open(_open)
1578
1579 self.assertRaises(IOError, shutil.copyfile, 'srcfile', 'destfile')
1580
1581 def test_w_dest_open_fails(self):
1582
1583 srcfile = self.Faux()
1584
1585 def _open(filename, mode='r'):
1586 if filename == 'srcfile':
1587 return srcfile
1588 if filename == 'destfile':
1589 raise IOError('Cannot open "destfile"')
1590 assert 0 # shouldn't reach here.
1591
1592 self._set_shutil_open(_open)
1593
1594 shutil.copyfile('srcfile', 'destfile')
1595 self.assertTrue(srcfile._entered)
1596 self.assertTrue(srcfile._exited_with[0] is IOError)
1597 self.assertEqual(srcfile._exited_with[1].args,
1598 ('Cannot open "destfile"',))
1599
1600 def test_w_dest_close_fails(self):
1601
1602 srcfile = self.Faux()
1603 destfile = self.Faux(True)
1604
1605 def _open(filename, mode='r'):
1606 if filename == 'srcfile':
1607 return srcfile
1608 if filename == 'destfile':
1609 return destfile
1610 assert 0 # shouldn't reach here.
1611
1612 self._set_shutil_open(_open)
1613
1614 shutil.copyfile('srcfile', 'destfile')
1615 self.assertTrue(srcfile._entered)
1616 self.assertTrue(destfile._entered)
1617 self.assertTrue(destfile._raised)
1618 self.assertTrue(srcfile._exited_with[0] is IOError)
1619 self.assertEqual(srcfile._exited_with[1].args,
1620 ('Cannot close',))
1621
1622 def test_w_source_close_fails(self):
1623
1624 srcfile = self.Faux(True)
1625 destfile = self.Faux()
1626
1627 def _open(filename, mode='r'):
1628 if filename == 'srcfile':
1629 return srcfile
1630 if filename == 'destfile':
1631 return destfile
1632 assert 0 # shouldn't reach here.
1633
1634 self._set_shutil_open(_open)
1635
1636 self.assertRaises(IOError,
1637 shutil.copyfile, 'srcfile', 'destfile')
1638 self.assertTrue(srcfile._entered)
1639 self.assertTrue(destfile._entered)
1640 self.assertFalse(destfile._raised)
1641 self.assertTrue(srcfile._exited_with[0] is None)
1642 self.assertTrue(srcfile._raised)
1643
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001644 def test_move_dir_caseinsensitive(self):
1645 # Renames a folder to the same name
1646 # but a different case.
1647
1648 self.src_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001649 self.addCleanup(shutil.rmtree, self.src_dir, True)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001650 dst_dir = os.path.join(
1651 os.path.dirname(self.src_dir),
1652 os.path.basename(self.src_dir).upper())
1653 self.assertNotEqual(self.src_dir, dst_dir)
1654
1655 try:
1656 shutil.move(self.src_dir, dst_dir)
1657 self.assertTrue(os.path.isdir(dst_dir))
1658 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +02001659 os.rmdir(dst_dir)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001660
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001661class TermsizeTests(unittest.TestCase):
1662 def test_does_not_crash(self):
1663 """Check if get_terminal_size() returns a meaningful value.
1664
1665 There's no easy portable way to actually check the size of the
1666 terminal, so let's check if it returns something sensible instead.
1667 """
1668 size = shutil.get_terminal_size()
Antoine Pitroucfade362012-02-08 23:48:59 +01001669 self.assertGreaterEqual(size.columns, 0)
1670 self.assertGreaterEqual(size.lines, 0)
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001671
1672 def test_os_environ_first(self):
1673 "Check if environment variables have precedence"
1674
1675 with support.EnvironmentVarGuard() as env:
1676 env['COLUMNS'] = '777'
1677 size = shutil.get_terminal_size()
1678 self.assertEqual(size.columns, 777)
1679
1680 with support.EnvironmentVarGuard() as env:
1681 env['LINES'] = '888'
1682 size = shutil.get_terminal_size()
1683 self.assertEqual(size.lines, 888)
1684
1685 @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
1686 def test_stty_match(self):
1687 """Check if stty returns the same results ignoring env
1688
1689 This test will fail if stdin and stdout are connected to
1690 different terminals with different sizes. Nevertheless, such
1691 situations should be pretty rare.
1692 """
1693 try:
1694 size = subprocess.check_output(['stty', 'size']).decode().split()
1695 except (FileNotFoundError, subprocess.CalledProcessError):
1696 self.skipTest("stty invocation failed")
1697 expected = (int(size[1]), int(size[0])) # reversed order
1698
1699 with support.EnvironmentVarGuard() as env:
1700 del env['LINES']
1701 del env['COLUMNS']
1702 actual = shutil.get_terminal_size()
1703
1704 self.assertEqual(expected, actual)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001705
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001706
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001707def test_main():
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001708 support.run_unittest(TestShutil, TestMove, TestCopyFile,
Brian Curtinc57a3452012-06-22 16:00:30 -05001709 TermsizeTests, TestWhich)
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001710
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001711if __name__ == '__main__':
Walter Dörwald21d3a322003-05-01 17:45:56 +00001712 test_main()