blob: 3b1b6949d066fc5a83393f5b86f358fceabeb49e [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
1355
Christian Heimesada8c3b2008-03-18 18:26:33 +00001356class TestMove(unittest.TestCase):
1357
1358 def setUp(self):
1359 filename = "foo"
1360 self.src_dir = tempfile.mkdtemp()
1361 self.dst_dir = tempfile.mkdtemp()
1362 self.src_file = os.path.join(self.src_dir, filename)
1363 self.dst_file = os.path.join(self.dst_dir, filename)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001364 with open(self.src_file, "wb") as f:
1365 f.write(b"spam")
1366
1367 def tearDown(self):
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001368 for d in (self.src_dir, self.dst_dir):
Christian Heimesada8c3b2008-03-18 18:26:33 +00001369 try:
1370 if d:
1371 shutil.rmtree(d)
1372 except:
1373 pass
1374
1375 def _check_move_file(self, src, dst, real_dst):
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001376 with open(src, "rb") as f:
1377 contents = f.read()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001378 shutil.move(src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001379 with open(real_dst, "rb") as f:
1380 self.assertEqual(contents, f.read())
Christian Heimesada8c3b2008-03-18 18:26:33 +00001381 self.assertFalse(os.path.exists(src))
1382
1383 def _check_move_dir(self, src, dst, real_dst):
1384 contents = sorted(os.listdir(src))
1385 shutil.move(src, dst)
1386 self.assertEqual(contents, sorted(os.listdir(real_dst)))
1387 self.assertFalse(os.path.exists(src))
1388
1389 def test_move_file(self):
1390 # Move a file to another location on the same filesystem.
1391 self._check_move_file(self.src_file, self.dst_file, self.dst_file)
1392
1393 def test_move_file_to_dir(self):
1394 # Move a file inside an existing dir on the same filesystem.
1395 self._check_move_file(self.src_file, self.dst_dir, self.dst_file)
1396
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001397 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001398 def test_move_file_other_fs(self):
1399 # Move a file to an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001400 self.test_move_file()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001401
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001402 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001403 def test_move_file_to_dir_other_fs(self):
1404 # Move a file to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001405 self.test_move_file_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001406
1407 def test_move_dir(self):
1408 # Move a dir to another location on the same filesystem.
1409 dst_dir = tempfile.mktemp()
1410 try:
1411 self._check_move_dir(self.src_dir, dst_dir, dst_dir)
1412 finally:
1413 try:
1414 shutil.rmtree(dst_dir)
1415 except:
1416 pass
1417
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001418 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001419 def test_move_dir_other_fs(self):
1420 # Move a dir to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001421 self.test_move_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001422
1423 def test_move_dir_to_dir(self):
1424 # Move a dir inside an existing dir on the same filesystem.
1425 self._check_move_dir(self.src_dir, self.dst_dir,
1426 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1427
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001428 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001429 def test_move_dir_to_dir_other_fs(self):
1430 # Move a dir inside an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001431 self.test_move_dir_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001432
1433 def test_existing_file_inside_dest_dir(self):
1434 # A file with the same name inside the destination dir already exists.
1435 with open(self.dst_file, "wb"):
1436 pass
1437 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir)
1438
1439 def test_dont_move_dir_in_itself(self):
1440 # Moving a dir inside itself raises an Error.
1441 dst = os.path.join(self.src_dir, "bar")
1442 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst)
1443
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001444 def test_destinsrc_false_negative(self):
1445 os.mkdir(TESTFN)
1446 try:
1447 for src, dst in [('srcdir', 'srcdir/dest')]:
1448 src = os.path.join(TESTFN, src)
1449 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001450 self.assertTrue(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001451 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001452 'dst (%s) is not in src (%s)' % (dst, src))
1453 finally:
1454 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001455
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001456 def test_destinsrc_false_positive(self):
1457 os.mkdir(TESTFN)
1458 try:
1459 for src, dst in [('srcdir', 'src/dest'), ('srcdir', 'srcdir.new')]:
1460 src = os.path.join(TESTFN, src)
1461 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001462 self.assertFalse(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001463 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001464 'dst (%s) is in src (%s)' % (dst, src))
1465 finally:
1466 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimes9bd667a2008-01-20 15:14:11 +00001467
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001468 @support.skip_unless_symlink
1469 @mock_rename
1470 def test_move_file_symlink(self):
1471 dst = os.path.join(self.src_dir, 'bar')
1472 os.symlink(self.src_file, dst)
1473 shutil.move(dst, self.dst_file)
1474 self.assertTrue(os.path.islink(self.dst_file))
1475 self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
1476
1477 @support.skip_unless_symlink
1478 @mock_rename
1479 def test_move_file_symlink_to_dir(self):
1480 filename = "bar"
1481 dst = os.path.join(self.src_dir, filename)
1482 os.symlink(self.src_file, dst)
1483 shutil.move(dst, self.dst_dir)
1484 final_link = os.path.join(self.dst_dir, filename)
1485 self.assertTrue(os.path.islink(final_link))
1486 self.assertTrue(os.path.samefile(self.src_file, final_link))
1487
1488 @support.skip_unless_symlink
1489 @mock_rename
1490 def test_move_dangling_symlink(self):
1491 src = os.path.join(self.src_dir, 'baz')
1492 dst = os.path.join(self.src_dir, 'bar')
1493 os.symlink(src, dst)
1494 dst_link = os.path.join(self.dst_dir, 'quux')
1495 shutil.move(dst, dst_link)
1496 self.assertTrue(os.path.islink(dst_link))
1497 self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
1498
1499 @support.skip_unless_symlink
1500 @mock_rename
1501 def test_move_dir_symlink(self):
1502 src = os.path.join(self.src_dir, 'baz')
1503 dst = os.path.join(self.src_dir, 'bar')
1504 os.mkdir(src)
1505 os.symlink(src, dst)
1506 dst_link = os.path.join(self.dst_dir, 'quux')
1507 shutil.move(dst, dst_link)
1508 self.assertTrue(os.path.islink(dst_link))
1509 self.assertTrue(os.path.samefile(src, dst_link))
1510
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001511 def test_move_return_value(self):
1512 rv = shutil.move(self.src_file, self.dst_dir)
1513 self.assertEqual(rv,
1514 os.path.join(self.dst_dir, os.path.basename(self.src_file)))
1515
1516 def test_move_as_rename_return_value(self):
1517 rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar'))
1518 self.assertEqual(rv, os.path.join(self.dst_dir, 'bar'))
1519
Tarek Ziadé5340db32010-04-19 22:30:51 +00001520
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001521class TestCopyFile(unittest.TestCase):
1522
1523 _delete = False
1524
1525 class Faux(object):
1526 _entered = False
1527 _exited_with = None
1528 _raised = False
1529 def __init__(self, raise_in_exit=False, suppress_at_exit=True):
1530 self._raise_in_exit = raise_in_exit
1531 self._suppress_at_exit = suppress_at_exit
1532 def read(self, *args):
1533 return ''
1534 def __enter__(self):
1535 self._entered = True
1536 def __exit__(self, exc_type, exc_val, exc_tb):
1537 self._exited_with = exc_type, exc_val, exc_tb
1538 if self._raise_in_exit:
1539 self._raised = True
1540 raise IOError("Cannot close")
1541 return self._suppress_at_exit
1542
1543 def tearDown(self):
1544 if self._delete:
1545 del shutil.open
1546
1547 def _set_shutil_open(self, func):
1548 shutil.open = func
1549 self._delete = True
1550
1551 def test_w_source_open_fails(self):
1552 def _open(filename, mode='r'):
1553 if filename == 'srcfile':
1554 raise IOError('Cannot open "srcfile"')
1555 assert 0 # shouldn't reach here.
1556
1557 self._set_shutil_open(_open)
1558
1559 self.assertRaises(IOError, shutil.copyfile, 'srcfile', 'destfile')
1560
1561 def test_w_dest_open_fails(self):
1562
1563 srcfile = self.Faux()
1564
1565 def _open(filename, mode='r'):
1566 if filename == 'srcfile':
1567 return srcfile
1568 if filename == 'destfile':
1569 raise IOError('Cannot open "destfile"')
1570 assert 0 # shouldn't reach here.
1571
1572 self._set_shutil_open(_open)
1573
1574 shutil.copyfile('srcfile', 'destfile')
1575 self.assertTrue(srcfile._entered)
1576 self.assertTrue(srcfile._exited_with[0] is IOError)
1577 self.assertEqual(srcfile._exited_with[1].args,
1578 ('Cannot open "destfile"',))
1579
1580 def test_w_dest_close_fails(self):
1581
1582 srcfile = self.Faux()
1583 destfile = self.Faux(True)
1584
1585 def _open(filename, mode='r'):
1586 if filename == 'srcfile':
1587 return srcfile
1588 if filename == 'destfile':
1589 return 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(destfile._entered)
1597 self.assertTrue(destfile._raised)
1598 self.assertTrue(srcfile._exited_with[0] is IOError)
1599 self.assertEqual(srcfile._exited_with[1].args,
1600 ('Cannot close',))
1601
1602 def test_w_source_close_fails(self):
1603
1604 srcfile = self.Faux(True)
1605 destfile = self.Faux()
1606
1607 def _open(filename, mode='r'):
1608 if filename == 'srcfile':
1609 return srcfile
1610 if filename == 'destfile':
1611 return destfile
1612 assert 0 # shouldn't reach here.
1613
1614 self._set_shutil_open(_open)
1615
1616 self.assertRaises(IOError,
1617 shutil.copyfile, 'srcfile', 'destfile')
1618 self.assertTrue(srcfile._entered)
1619 self.assertTrue(destfile._entered)
1620 self.assertFalse(destfile._raised)
1621 self.assertTrue(srcfile._exited_with[0] is None)
1622 self.assertTrue(srcfile._raised)
1623
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001624 def test_move_dir_caseinsensitive(self):
1625 # Renames a folder to the same name
1626 # but a different case.
1627
1628 self.src_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001629 self.addCleanup(shutil.rmtree, self.src_dir, True)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001630 dst_dir = os.path.join(
1631 os.path.dirname(self.src_dir),
1632 os.path.basename(self.src_dir).upper())
1633 self.assertNotEqual(self.src_dir, dst_dir)
1634
1635 try:
1636 shutil.move(self.src_dir, dst_dir)
1637 self.assertTrue(os.path.isdir(dst_dir))
1638 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +02001639 os.rmdir(dst_dir)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001640
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001641class TermsizeTests(unittest.TestCase):
1642 def test_does_not_crash(self):
1643 """Check if get_terminal_size() returns a meaningful value.
1644
1645 There's no easy portable way to actually check the size of the
1646 terminal, so let's check if it returns something sensible instead.
1647 """
1648 size = shutil.get_terminal_size()
Antoine Pitroucfade362012-02-08 23:48:59 +01001649 self.assertGreaterEqual(size.columns, 0)
1650 self.assertGreaterEqual(size.lines, 0)
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001651
1652 def test_os_environ_first(self):
1653 "Check if environment variables have precedence"
1654
1655 with support.EnvironmentVarGuard() as env:
1656 env['COLUMNS'] = '777'
1657 size = shutil.get_terminal_size()
1658 self.assertEqual(size.columns, 777)
1659
1660 with support.EnvironmentVarGuard() as env:
1661 env['LINES'] = '888'
1662 size = shutil.get_terminal_size()
1663 self.assertEqual(size.lines, 888)
1664
1665 @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
1666 def test_stty_match(self):
1667 """Check if stty returns the same results ignoring env
1668
1669 This test will fail if stdin and stdout are connected to
1670 different terminals with different sizes. Nevertheless, such
1671 situations should be pretty rare.
1672 """
1673 try:
1674 size = subprocess.check_output(['stty', 'size']).decode().split()
1675 except (FileNotFoundError, subprocess.CalledProcessError):
1676 self.skipTest("stty invocation failed")
1677 expected = (int(size[1]), int(size[0])) # reversed order
1678
1679 with support.EnvironmentVarGuard() as env:
1680 del env['LINES']
1681 del env['COLUMNS']
1682 actual = shutil.get_terminal_size()
1683
1684 self.assertEqual(expected, actual)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001685
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001686
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001687def test_main():
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001688 support.run_unittest(TestShutil, TestMove, TestCopyFile,
Brian Curtinc57a3452012-06-22 16:00:30 -05001689 TermsizeTests, TestWhich)
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001690
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001691if __name__ == '__main__':
Walter Dörwald21d3a322003-05-01 17:45:56 +00001692 test_main()