blob: 98ea6d19c031ce4f8b2b63d3b9c0c3b06dd1ed2f [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 Schlawack48653762012-10-07 12:49:58 +020021 unregister_unpack_format, get_unpack_formats,
22 SameFileError)
Tarek Ziadé396fad72010-02-23 05:30:31 +000023import tarfile
24import warnings
25
26from test import support
Ezio Melotti975077a2011-05-19 22:03:22 +030027from test.support import TESTFN, check_warnings, captured_stdout, requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +000028
Tarek Ziadéffa155a2010-04-29 13:34:35 +000029try:
30 import bz2
31 BZ2_SUPPORTED = True
32except ImportError:
33 BZ2_SUPPORTED = False
34
Antoine Pitrou7fff0962009-05-01 21:09:44 +000035TESTFN2 = TESTFN + "2"
Barry Warsaw7fc2cca2003-01-24 17:34:13 +000036
Tarek Ziadé396fad72010-02-23 05:30:31 +000037try:
38 import grp
39 import pwd
40 UID_GID_SUPPORT = True
41except ImportError:
42 UID_GID_SUPPORT = False
43
44try:
Tarek Ziadé396fad72010-02-23 05:30:31 +000045 import zipfile
46 ZIP_SUPPORT = True
47except ImportError:
48 ZIP_SUPPORT = find_executable('zip')
49
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040050def _fake_rename(*args, **kwargs):
51 # Pretend the destination path is on a different filesystem.
Antoine Pitrouc041ab62012-01-02 19:18:02 +010052 raise OSError(getattr(errno, 'EXDEV', 18), "Invalid cross-device link")
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040053
54def mock_rename(func):
55 @functools.wraps(func)
56 def wrap(*args, **kwargs):
57 try:
58 builtin_rename = os.rename
59 os.rename = _fake_rename
60 return func(*args, **kwargs)
61 finally:
62 os.rename = builtin_rename
63 return wrap
64
Éric Araujoa7e33a12011-08-12 19:51:35 +020065def write_file(path, content, binary=False):
66 """Write *content* to a file located at *path*.
67
68 If *path* is a tuple instead of a string, os.path.join will be used to
69 make a path. If *binary* is true, the file will be opened in binary
70 mode.
71 """
72 if isinstance(path, tuple):
73 path = os.path.join(*path)
74 with open(path, 'wb' if binary else 'w') as fp:
75 fp.write(content)
76
77def read_file(path, binary=False):
78 """Return contents from a file located at *path*.
79
80 If *path* is a tuple instead of a string, os.path.join will be used to
81 make a path. If *binary* is true, the file will be opened in binary
82 mode.
83 """
84 if isinstance(path, tuple):
85 path = os.path.join(*path)
86 with open(path, 'rb' if binary else 'r') as fp:
87 return fp.read()
88
89
Barry Warsaw7fc2cca2003-01-24 17:34:13 +000090class TestShutil(unittest.TestCase):
Tarek Ziadé396fad72010-02-23 05:30:31 +000091
92 def setUp(self):
93 super(TestShutil, self).setUp()
94 self.tempdirs = []
95
96 def tearDown(self):
97 super(TestShutil, self).tearDown()
98 while self.tempdirs:
99 d = self.tempdirs.pop()
100 shutil.rmtree(d, os.name in ('nt', 'cygwin'))
101
Tarek Ziadé396fad72010-02-23 05:30:31 +0000102
103 def mkdtemp(self):
104 """Create a temporary directory that will be cleaned up.
105
106 Returns the path of the directory.
107 """
108 d = tempfile.mkdtemp()
109 self.tempdirs.append(d)
110 return d
Tarek Ziadé5340db32010-04-19 22:30:51 +0000111
Hynek Schlawack3b527782012-06-25 13:27:31 +0200112 def test_rmtree_works_on_bytes(self):
113 tmp = self.mkdtemp()
114 victim = os.path.join(tmp, 'killme')
115 os.mkdir(victim)
116 write_file(os.path.join(victim, 'somefile'), 'foo')
117 victim = os.fsencode(victim)
118 self.assertIsInstance(victim, bytes)
119 shutil.rmtree(victim)
120
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200121 @support.skip_unless_symlink
122 def test_rmtree_fails_on_symlink(self):
123 tmp = self.mkdtemp()
124 dir_ = os.path.join(tmp, 'dir')
125 os.mkdir(dir_)
126 link = os.path.join(tmp, 'link')
127 os.symlink(dir_, link)
128 self.assertRaises(OSError, shutil.rmtree, link)
129 self.assertTrue(os.path.exists(dir_))
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100130 self.assertTrue(os.path.lexists(link))
131 errors = []
132 def onerror(*args):
133 errors.append(args)
134 shutil.rmtree(link, onerror=onerror)
135 self.assertEqual(len(errors), 1)
136 self.assertIs(errors[0][0], os.path.islink)
137 self.assertEqual(errors[0][1], link)
138 self.assertIsInstance(errors[0][2][1], OSError)
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200139
140 @support.skip_unless_symlink
141 def test_rmtree_works_on_symlinks(self):
142 tmp = self.mkdtemp()
143 dir1 = os.path.join(tmp, 'dir1')
144 dir2 = os.path.join(dir1, 'dir2')
145 dir3 = os.path.join(tmp, 'dir3')
146 for d in dir1, dir2, dir3:
147 os.mkdir(d)
148 file1 = os.path.join(tmp, 'file1')
149 write_file(file1, 'foo')
150 link1 = os.path.join(dir1, 'link1')
151 os.symlink(dir2, link1)
152 link2 = os.path.join(dir1, 'link2')
153 os.symlink(dir3, link2)
154 link3 = os.path.join(dir1, 'link3')
155 os.symlink(file1, link3)
156 # make sure symlinks are removed but not followed
157 shutil.rmtree(dir1)
158 self.assertFalse(os.path.exists(dir1))
159 self.assertTrue(os.path.exists(dir3))
160 self.assertTrue(os.path.exists(file1))
161
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000162 def test_rmtree_errors(self):
163 # filename is guaranteed not to exist
164 filename = tempfile.mktemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100165 self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
166 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100167 shutil.rmtree(filename, ignore_errors=True)
168
169 # existing file
170 tmpdir = self.mkdtemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100171 write_file((tmpdir, "tstfile"), "")
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100172 filename = os.path.join(tmpdir, "tstfile")
Hynek Schlawackb5501102012-12-10 09:11:25 +0100173 with self.assertRaises(NotADirectoryError) as cm:
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100174 shutil.rmtree(filename)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100175 # The reason for this rather odd construct is that Windows sprinkles
176 # a \*.* at the end of file names. But only sometimes on some buildbots
177 possible_args = [filename, os.path.join(filename, '*.*')]
178 self.assertIn(cm.exception.filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100179 self.assertTrue(os.path.exists(filename))
Hynek Schlawackb5501102012-12-10 09:11:25 +0100180 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100181 shutil.rmtree(filename, ignore_errors=True)
182 self.assertTrue(os.path.exists(filename))
183 errors = []
184 def onerror(*args):
185 errors.append(args)
186 shutil.rmtree(filename, onerror=onerror)
187 self.assertEqual(len(errors), 2)
188 self.assertIs(errors[0][0], os.listdir)
189 self.assertEqual(errors[0][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100190 self.assertIsInstance(errors[0][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100191 self.assertIn(errors[0][2][1].filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100192 self.assertIs(errors[1][0], os.rmdir)
193 self.assertEqual(errors[1][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100194 self.assertIsInstance(errors[1][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100195 self.assertIn(errors[1][2][1].filename, possible_args)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000196
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000197
Serhiy Storchaka43767632013-11-03 21:31:38 +0200198 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod()')
199 @unittest.skipIf(sys.platform[:6] == 'cygwin',
200 "This test can't be run on Cygwin (issue #1071513).")
201 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
202 "This test can't be run reliably as root (issue #1076467).")
203 def test_on_error(self):
204 self.errorState = 0
205 os.mkdir(TESTFN)
206 self.addCleanup(shutil.rmtree, TESTFN)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200207
Serhiy Storchaka43767632013-11-03 21:31:38 +0200208 self.child_file_path = os.path.join(TESTFN, 'a')
209 self.child_dir_path = os.path.join(TESTFN, 'b')
210 support.create_empty_file(self.child_file_path)
211 os.mkdir(self.child_dir_path)
212 old_dir_mode = os.stat(TESTFN).st_mode
213 old_child_file_mode = os.stat(self.child_file_path).st_mode
214 old_child_dir_mode = os.stat(self.child_dir_path).st_mode
215 # Make unwritable.
216 new_mode = stat.S_IREAD|stat.S_IEXEC
217 os.chmod(self.child_file_path, new_mode)
218 os.chmod(self.child_dir_path, new_mode)
219 os.chmod(TESTFN, new_mode)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000220
Serhiy Storchaka43767632013-11-03 21:31:38 +0200221 self.addCleanup(os.chmod, TESTFN, old_dir_mode)
222 self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
223 self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200224
Serhiy Storchaka43767632013-11-03 21:31:38 +0200225 shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
226 # Test whether onerror has actually been called.
227 self.assertEqual(self.errorState, 3,
228 "Expected call to onerror function did not happen.")
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000229
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000230 def check_args_to_onerror(self, func, arg, exc):
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000231 # test_rmtree_errors deliberately runs rmtree
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200232 # on a directory that is chmod 500, which will fail.
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000233 # This function is run when shutil.rmtree fails.
234 # 99.9% of the time it initially fails to remove
235 # a file in the directory, so the first time through
236 # func is os.remove.
237 # However, some Linux machines running ZFS on
238 # FUSE experienced a failure earlier in the process
239 # at os.listdir. The first failure may legally
240 # be either.
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200241 if self.errorState < 2:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200242 if func is os.unlink:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200243 self.assertEqual(arg, self.child_file_path)
244 elif func is os.rmdir:
245 self.assertEqual(arg, self.child_dir_path)
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000246 else:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200247 self.assertIs(func, os.listdir)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200248 self.assertIn(arg, [TESTFN, self.child_dir_path])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000249 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200250 self.errorState += 1
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000251 else:
252 self.assertEqual(func, os.rmdir)
253 self.assertEqual(arg, TESTFN)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000254 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200255 self.errorState = 3
256
257 def test_rmtree_does_not_choke_on_failing_lstat(self):
258 try:
259 orig_lstat = os.lstat
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200260 def raiser(fn, *args, **kwargs):
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200261 if fn != TESTFN:
262 raise OSError()
263 else:
264 return orig_lstat(fn)
265 os.lstat = raiser
266
267 os.mkdir(TESTFN)
268 write_file((TESTFN, 'foo'), 'foo')
269 shutil.rmtree(TESTFN)
270 finally:
271 os.lstat = orig_lstat
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000272
Antoine Pitrou78091e62011-12-29 18:54:15 +0100273 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
274 @support.skip_unless_symlink
275 def test_copymode_follow_symlinks(self):
276 tmp_dir = self.mkdtemp()
277 src = os.path.join(tmp_dir, 'foo')
278 dst = os.path.join(tmp_dir, 'bar')
279 src_link = os.path.join(tmp_dir, 'baz')
280 dst_link = os.path.join(tmp_dir, 'quux')
281 write_file(src, 'foo')
282 write_file(dst, 'foo')
283 os.symlink(src, src_link)
284 os.symlink(dst, dst_link)
285 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
286 # file to file
287 os.chmod(dst, stat.S_IRWXO)
288 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
289 shutil.copymode(src, dst)
290 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
291 # follow src link
292 os.chmod(dst, stat.S_IRWXO)
293 shutil.copymode(src_link, dst)
294 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
295 # follow dst link
296 os.chmod(dst, stat.S_IRWXO)
297 shutil.copymode(src, dst_link)
298 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
299 # follow both links
300 os.chmod(dst, stat.S_IRWXO)
301 shutil.copymode(src_link, dst)
302 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
303
304 @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
305 @support.skip_unless_symlink
306 def test_copymode_symlink_to_symlink(self):
307 tmp_dir = self.mkdtemp()
308 src = os.path.join(tmp_dir, 'foo')
309 dst = os.path.join(tmp_dir, 'bar')
310 src_link = os.path.join(tmp_dir, 'baz')
311 dst_link = os.path.join(tmp_dir, 'quux')
312 write_file(src, 'foo')
313 write_file(dst, 'foo')
314 os.symlink(src, src_link)
315 os.symlink(dst, dst_link)
316 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
317 os.chmod(dst, stat.S_IRWXU)
318 os.lchmod(src_link, stat.S_IRWXO|stat.S_IRWXG)
319 # link to link
320 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700321 shutil.copymode(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100322 self.assertEqual(os.lstat(src_link).st_mode,
323 os.lstat(dst_link).st_mode)
324 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
325 # src link - use chmod
326 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700327 shutil.copymode(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100328 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
329 # dst link - use chmod
330 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700331 shutil.copymode(src, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100332 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
333
334 @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing')
335 @support.skip_unless_symlink
336 def test_copymode_symlink_to_symlink_wo_lchmod(self):
337 tmp_dir = self.mkdtemp()
338 src = os.path.join(tmp_dir, 'foo')
339 dst = os.path.join(tmp_dir, 'bar')
340 src_link = os.path.join(tmp_dir, 'baz')
341 dst_link = os.path.join(tmp_dir, 'quux')
342 write_file(src, 'foo')
343 write_file(dst, 'foo')
344 os.symlink(src, src_link)
345 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700346 shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail
Antoine Pitrou78091e62011-12-29 18:54:15 +0100347
348 @support.skip_unless_symlink
349 def test_copystat_symlinks(self):
350 tmp_dir = self.mkdtemp()
351 src = os.path.join(tmp_dir, 'foo')
352 dst = os.path.join(tmp_dir, 'bar')
353 src_link = os.path.join(tmp_dir, 'baz')
354 dst_link = os.path.join(tmp_dir, 'qux')
355 write_file(src, 'foo')
356 src_stat = os.stat(src)
357 os.utime(src, (src_stat.st_atime,
358 src_stat.st_mtime - 42.0)) # ensure different mtimes
359 write_file(dst, 'bar')
360 self.assertNotEqual(os.stat(src).st_mtime, os.stat(dst).st_mtime)
361 os.symlink(src, src_link)
362 os.symlink(dst, dst_link)
363 if hasattr(os, 'lchmod'):
364 os.lchmod(src_link, stat.S_IRWXO)
365 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
366 os.lchflags(src_link, stat.UF_NODUMP)
367 src_link_stat = os.lstat(src_link)
368 # follow
369 if hasattr(os, 'lchmod'):
Larry Hastingsb4038062012-07-15 10:57:38 -0700370 shutil.copystat(src_link, dst_link, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100371 self.assertNotEqual(src_link_stat.st_mode, os.stat(dst).st_mode)
372 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700373 shutil.copystat(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100374 dst_link_stat = os.lstat(dst_link)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700375 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100376 for attr in 'st_atime', 'st_mtime':
377 # The modification times may be truncated in the new file.
378 self.assertLessEqual(getattr(src_link_stat, attr),
379 getattr(dst_link_stat, attr) + 1)
380 if hasattr(os, 'lchmod'):
381 self.assertEqual(src_link_stat.st_mode, dst_link_stat.st_mode)
382 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
383 self.assertEqual(src_link_stat.st_flags, dst_link_stat.st_flags)
384 # tell to follow but dst is not a link
Larry Hastingsb4038062012-07-15 10:57:38 -0700385 shutil.copystat(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100386 self.assertTrue(abs(os.stat(src).st_mtime - os.stat(dst).st_mtime) <
387 00000.1)
388
Ned Deilybaf75712012-05-10 17:05:19 -0700389 @unittest.skipUnless(hasattr(os, 'chflags') and
390 hasattr(errno, 'EOPNOTSUPP') and
391 hasattr(errno, 'ENOTSUP'),
392 "requires os.chflags, EOPNOTSUPP & ENOTSUP")
393 def test_copystat_handles_harmless_chflags_errors(self):
394 tmpdir = self.mkdtemp()
395 file1 = os.path.join(tmpdir, 'file1')
396 file2 = os.path.join(tmpdir, 'file2')
397 write_file(file1, 'xxx')
398 write_file(file2, 'xxx')
399
400 def make_chflags_raiser(err):
401 ex = OSError()
402
Larry Hastings90867a52012-06-22 17:01:41 -0700403 def _chflags_raiser(path, flags, *, follow_symlinks=True):
Ned Deilybaf75712012-05-10 17:05:19 -0700404 ex.errno = err
405 raise ex
406 return _chflags_raiser
407 old_chflags = os.chflags
408 try:
409 for err in errno.EOPNOTSUPP, errno.ENOTSUP:
410 os.chflags = make_chflags_raiser(err)
411 shutil.copystat(file1, file2)
412 # assert others errors break it
413 os.chflags = make_chflags_raiser(errno.EOPNOTSUPP + errno.ENOTSUP)
414 self.assertRaises(OSError, shutil.copystat, file1, file2)
415 finally:
416 os.chflags = old_chflags
417
Antoine Pitrou424246f2012-05-12 19:02:01 +0200418 @support.skip_unless_xattr
419 def test_copyxattr(self):
420 tmp_dir = self.mkdtemp()
421 src = os.path.join(tmp_dir, 'foo')
422 write_file(src, 'foo')
423 dst = os.path.join(tmp_dir, 'bar')
424 write_file(dst, 'bar')
425
426 # no xattr == no problem
427 shutil._copyxattr(src, dst)
428 # common case
429 os.setxattr(src, 'user.foo', b'42')
430 os.setxattr(src, 'user.bar', b'43')
431 shutil._copyxattr(src, dst)
432 self.assertEqual(os.listxattr(src), os.listxattr(dst))
433 self.assertEqual(
434 os.getxattr(src, 'user.foo'),
435 os.getxattr(dst, 'user.foo'))
436 # check errors don't affect other attrs
437 os.remove(dst)
438 write_file(dst, 'bar')
439 os_error = OSError(errno.EPERM, 'EPERM')
440
Larry Hastings9cf065c2012-06-22 16:30:09 -0700441 def _raise_on_user_foo(fname, attr, val, **kwargs):
Antoine Pitrou424246f2012-05-12 19:02:01 +0200442 if attr == 'user.foo':
443 raise os_error
444 else:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700445 orig_setxattr(fname, attr, val, **kwargs)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200446 try:
447 orig_setxattr = os.setxattr
448 os.setxattr = _raise_on_user_foo
449 shutil._copyxattr(src, dst)
Antoine Pitrou61597d32012-05-12 23:37:35 +0200450 self.assertIn('user.bar', os.listxattr(dst))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200451 finally:
452 os.setxattr = orig_setxattr
Hynek Schlawack0beab052013-02-05 08:22:44 +0100453 # the source filesystem not supporting xattrs should be ok, too.
454 def _raise_on_src(fname, *, follow_symlinks=True):
455 if fname == src:
456 raise OSError(errno.ENOTSUP, 'Operation not supported')
457 return orig_listxattr(fname, follow_symlinks=follow_symlinks)
458 try:
459 orig_listxattr = os.listxattr
460 os.listxattr = _raise_on_src
461 shutil._copyxattr(src, dst)
462 finally:
463 os.listxattr = orig_listxattr
Antoine Pitrou424246f2012-05-12 19:02:01 +0200464
Larry Hastingsad5ae042012-07-14 17:55:11 -0700465 # test that shutil.copystat copies xattrs
466 src = os.path.join(tmp_dir, 'the_original')
467 write_file(src, src)
468 os.setxattr(src, 'user.the_value', b'fiddly')
469 dst = os.path.join(tmp_dir, 'the_copy')
470 write_file(dst, dst)
471 shutil.copystat(src, dst)
Hynek Schlawackc2d481f2012-07-16 17:11:10 +0200472 self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700473
Antoine Pitrou424246f2012-05-12 19:02:01 +0200474 @support.skip_unless_symlink
475 @support.skip_unless_xattr
476 @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,
477 'root privileges required')
478 def test_copyxattr_symlinks(self):
479 # On Linux, it's only possible to access non-user xattr for symlinks;
480 # which in turn require root privileges. This test should be expanded
481 # as soon as other platforms gain support for extended attributes.
482 tmp_dir = self.mkdtemp()
483 src = os.path.join(tmp_dir, 'foo')
484 src_link = os.path.join(tmp_dir, 'baz')
485 write_file(src, 'foo')
486 os.symlink(src, src_link)
487 os.setxattr(src, 'trusted.foo', b'42')
Larry Hastings9cf065c2012-06-22 16:30:09 -0700488 os.setxattr(src_link, 'trusted.foo', b'43', follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200489 dst = os.path.join(tmp_dir, 'bar')
490 dst_link = os.path.join(tmp_dir, 'qux')
491 write_file(dst, 'bar')
492 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700493 shutil._copyxattr(src_link, dst_link, follow_symlinks=False)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700494 self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43')
Antoine Pitrou424246f2012-05-12 19:02:01 +0200495 self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo')
Larry Hastingsb4038062012-07-15 10:57:38 -0700496 shutil._copyxattr(src_link, dst, follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200497 self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43')
498
Antoine Pitrou78091e62011-12-29 18:54:15 +0100499 @support.skip_unless_symlink
500 def test_copy_symlinks(self):
501 tmp_dir = self.mkdtemp()
502 src = os.path.join(tmp_dir, 'foo')
503 dst = os.path.join(tmp_dir, 'bar')
504 src_link = os.path.join(tmp_dir, 'baz')
505 write_file(src, 'foo')
506 os.symlink(src, src_link)
507 if hasattr(os, 'lchmod'):
508 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
509 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700510 shutil.copy(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100511 self.assertFalse(os.path.islink(dst))
512 self.assertEqual(read_file(src), read_file(dst))
513 os.remove(dst)
514 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700515 shutil.copy(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100516 self.assertTrue(os.path.islink(dst))
517 self.assertEqual(os.readlink(dst), os.readlink(src_link))
518 if hasattr(os, 'lchmod'):
519 self.assertEqual(os.lstat(src_link).st_mode,
520 os.lstat(dst).st_mode)
521
522 @support.skip_unless_symlink
523 def test_copy2_symlinks(self):
524 tmp_dir = self.mkdtemp()
525 src = os.path.join(tmp_dir, 'foo')
526 dst = os.path.join(tmp_dir, 'bar')
527 src_link = os.path.join(tmp_dir, 'baz')
528 write_file(src, 'foo')
529 os.symlink(src, src_link)
530 if hasattr(os, 'lchmod'):
531 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
532 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
533 os.lchflags(src_link, stat.UF_NODUMP)
534 src_stat = os.stat(src)
535 src_link_stat = os.lstat(src_link)
536 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700537 shutil.copy2(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100538 self.assertFalse(os.path.islink(dst))
539 self.assertEqual(read_file(src), read_file(dst))
540 os.remove(dst)
541 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700542 shutil.copy2(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100543 self.assertTrue(os.path.islink(dst))
544 self.assertEqual(os.readlink(dst), os.readlink(src_link))
545 dst_stat = os.lstat(dst)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700546 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100547 for attr in 'st_atime', 'st_mtime':
548 # The modification times may be truncated in the new file.
549 self.assertLessEqual(getattr(src_link_stat, attr),
550 getattr(dst_stat, attr) + 1)
551 if hasattr(os, 'lchmod'):
552 self.assertEqual(src_link_stat.st_mode, dst_stat.st_mode)
553 self.assertNotEqual(src_stat.st_mode, dst_stat.st_mode)
554 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
555 self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags)
556
Antoine Pitrou424246f2012-05-12 19:02:01 +0200557 @support.skip_unless_xattr
558 def test_copy2_xattr(self):
559 tmp_dir = self.mkdtemp()
560 src = os.path.join(tmp_dir, 'foo')
561 dst = os.path.join(tmp_dir, 'bar')
562 write_file(src, 'foo')
563 os.setxattr(src, 'user.foo', b'42')
564 shutil.copy2(src, dst)
565 self.assertEqual(
566 os.getxattr(src, 'user.foo'),
567 os.getxattr(dst, 'user.foo'))
568 os.remove(dst)
569
Antoine Pitrou78091e62011-12-29 18:54:15 +0100570 @support.skip_unless_symlink
571 def test_copyfile_symlinks(self):
572 tmp_dir = self.mkdtemp()
573 src = os.path.join(tmp_dir, 'src')
574 dst = os.path.join(tmp_dir, 'dst')
575 dst_link = os.path.join(tmp_dir, 'dst_link')
576 link = os.path.join(tmp_dir, 'link')
577 write_file(src, 'foo')
578 os.symlink(src, link)
579 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700580 shutil.copyfile(link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100581 self.assertTrue(os.path.islink(dst_link))
582 self.assertEqual(os.readlink(link), os.readlink(dst_link))
583 # follow
584 shutil.copyfile(link, dst)
585 self.assertFalse(os.path.islink(dst))
586
Hynek Schlawack2100b422012-06-23 20:28:32 +0200587 def test_rmtree_uses_safe_fd_version_if_available(self):
Hynek Schlawackd0f6e0a2012-06-29 08:28:20 +0200588 _use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
589 os.supports_dir_fd and
590 os.listdir in os.supports_fd and
591 os.stat in os.supports_follow_symlinks)
592 if _use_fd_functions:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200593 self.assertTrue(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000594 self.assertTrue(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200595 tmp_dir = self.mkdtemp()
596 d = os.path.join(tmp_dir, 'a')
597 os.mkdir(d)
598 try:
599 real_rmtree = shutil._rmtree_safe_fd
600 class Called(Exception): pass
601 def _raiser(*args, **kwargs):
602 raise Called
603 shutil._rmtree_safe_fd = _raiser
604 self.assertRaises(Called, shutil.rmtree, d)
605 finally:
606 shutil._rmtree_safe_fd = real_rmtree
607 else:
608 self.assertFalse(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000609 self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200610
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000611 def test_rmtree_dont_delete_file(self):
612 # When called on a file instead of a directory, don't delete it.
613 handle, path = tempfile.mkstemp()
Victor Stinnerbf816222011-06-30 23:25:47 +0200614 os.close(handle)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200615 self.assertRaises(NotADirectoryError, shutil.rmtree, path)
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000616 os.remove(path)
617
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000618 def test_copytree_simple(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000619 src_dir = tempfile.mkdtemp()
620 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200621 self.addCleanup(shutil.rmtree, src_dir)
622 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
623 write_file((src_dir, 'test.txt'), '123')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000624 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200625 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000626
Éric Araujoa7e33a12011-08-12 19:51:35 +0200627 shutil.copytree(src_dir, dst_dir)
628 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test.txt')))
629 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'test_dir')))
630 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test_dir',
631 'test.txt')))
632 actual = read_file((dst_dir, 'test.txt'))
633 self.assertEqual(actual, '123')
634 actual = read_file((dst_dir, 'test_dir', 'test.txt'))
635 self.assertEqual(actual, '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000636
Antoine Pitrou78091e62011-12-29 18:54:15 +0100637 @support.skip_unless_symlink
638 def test_copytree_symlinks(self):
639 tmp_dir = self.mkdtemp()
640 src_dir = os.path.join(tmp_dir, 'src')
641 dst_dir = os.path.join(tmp_dir, 'dst')
642 sub_dir = os.path.join(src_dir, 'sub')
643 os.mkdir(src_dir)
644 os.mkdir(sub_dir)
645 write_file((src_dir, 'file.txt'), 'foo')
646 src_link = os.path.join(sub_dir, 'link')
647 dst_link = os.path.join(dst_dir, 'sub/link')
648 os.symlink(os.path.join(src_dir, 'file.txt'),
649 src_link)
650 if hasattr(os, 'lchmod'):
651 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
652 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
653 os.lchflags(src_link, stat.UF_NODUMP)
654 src_stat = os.lstat(src_link)
655 shutil.copytree(src_dir, dst_dir, symlinks=True)
656 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'sub', 'link')))
657 self.assertEqual(os.readlink(os.path.join(dst_dir, 'sub', 'link')),
658 os.path.join(src_dir, 'file.txt'))
659 dst_stat = os.lstat(dst_link)
660 if hasattr(os, 'lchmod'):
661 self.assertEqual(dst_stat.st_mode, src_stat.st_mode)
662 if hasattr(os, 'lchflags'):
663 self.assertEqual(dst_stat.st_flags, src_stat.st_flags)
664
Georg Brandl2ee470f2008-07-16 12:55:28 +0000665 def test_copytree_with_exclude(self):
Georg Brandl2ee470f2008-07-16 12:55:28 +0000666 # creating data
667 join = os.path.join
668 exists = os.path.exists
669 src_dir = tempfile.mkdtemp()
Georg Brandl2ee470f2008-07-16 12:55:28 +0000670 try:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000671 dst_dir = join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200672 write_file((src_dir, 'test.txt'), '123')
673 write_file((src_dir, 'test.tmp'), '123')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000674 os.mkdir(join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200675 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000676 os.mkdir(join(src_dir, 'test_dir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200677 write_file((src_dir, 'test_dir2', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000678 os.mkdir(join(src_dir, 'test_dir2', 'subdir'))
679 os.mkdir(join(src_dir, 'test_dir2', 'subdir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200680 write_file((src_dir, 'test_dir2', 'subdir', 'test.txt'), '456')
681 write_file((src_dir, 'test_dir2', 'subdir2', 'test.py'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000682
683 # testing glob-like patterns
684 try:
685 patterns = shutil.ignore_patterns('*.tmp', 'test_dir2')
686 shutil.copytree(src_dir, dst_dir, ignore=patterns)
687 # checking the result: some elements should not be copied
688 self.assertTrue(exists(join(dst_dir, 'test.txt')))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200689 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
690 self.assertFalse(exists(join(dst_dir, 'test_dir2')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000691 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200692 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000693 try:
694 patterns = shutil.ignore_patterns('*.tmp', 'subdir*')
695 shutil.copytree(src_dir, dst_dir, ignore=patterns)
696 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200697 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
698 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2')))
699 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000700 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200701 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000702
703 # testing callable-style
704 try:
705 def _filter(src, names):
706 res = []
707 for name in names:
708 path = os.path.join(src, name)
709
710 if (os.path.isdir(path) and
711 path.split()[-1] == 'subdir'):
712 res.append(name)
713 elif os.path.splitext(path)[-1] in ('.py'):
714 res.append(name)
715 return res
716
717 shutil.copytree(src_dir, dst_dir, ignore=_filter)
718
719 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200720 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2',
721 'test.py')))
722 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000723
724 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200725 shutil.rmtree(dst_dir)
Georg Brandl2ee470f2008-07-16 12:55:28 +0000726 finally:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000727 shutil.rmtree(src_dir)
728 shutil.rmtree(os.path.dirname(dst_dir))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000729
Antoine Pitrouac601602013-08-16 19:35:02 +0200730 def test_copytree_retains_permissions(self):
731 tmp_dir = tempfile.mkdtemp()
732 src_dir = os.path.join(tmp_dir, 'source')
733 os.mkdir(src_dir)
734 dst_dir = os.path.join(tmp_dir, 'destination')
735 self.addCleanup(shutil.rmtree, tmp_dir)
736
737 os.chmod(src_dir, 0o777)
738 write_file((src_dir, 'permissive.txt'), '123')
739 os.chmod(os.path.join(src_dir, 'permissive.txt'), 0o777)
740 write_file((src_dir, 'restrictive.txt'), '456')
741 os.chmod(os.path.join(src_dir, 'restrictive.txt'), 0o600)
742 restrictive_subdir = tempfile.mkdtemp(dir=src_dir)
743 os.chmod(restrictive_subdir, 0o600)
744
745 shutil.copytree(src_dir, dst_dir)
Brett Cannon9c7eb552013-08-23 14:38:11 -0400746 self.assertEqual(os.stat(src_dir).st_mode, os.stat(dst_dir).st_mode)
747 self.assertEqual(os.stat(os.path.join(src_dir, 'permissive.txt')).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200748 os.stat(os.path.join(dst_dir, 'permissive.txt')).st_mode)
Brett Cannon9c7eb552013-08-23 14:38:11 -0400749 self.assertEqual(os.stat(os.path.join(src_dir, 'restrictive.txt')).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200750 os.stat(os.path.join(dst_dir, 'restrictive.txt')).st_mode)
751 restrictive_subdir_dst = os.path.join(dst_dir,
752 os.path.split(restrictive_subdir)[1])
Brett Cannon9c7eb552013-08-23 14:38:11 -0400753 self.assertEqual(os.stat(restrictive_subdir).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200754 os.stat(restrictive_subdir_dst).st_mode)
755
Zachary Ware9fe6d862013-12-08 00:20:35 -0600756 @unittest.skipIf(os.name == 'nt', 'temporarily disabled on Windows')
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000757 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000758 def test_dont_copy_file_onto_link_to_itself(self):
759 # bug 851123.
760 os.mkdir(TESTFN)
761 src = os.path.join(TESTFN, 'cheese')
762 dst = os.path.join(TESTFN, 'shop')
763 try:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000764 with open(src, 'w') as f:
765 f.write('cheddar')
766 os.link(src, dst)
Hynek Schlawack48653762012-10-07 12:49:58 +0200767 self.assertRaises(shutil.SameFileError, shutil.copyfile, src, dst)
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000768 with open(src, 'r') as f:
769 self.assertEqual(f.read(), 'cheddar')
770 os.remove(dst)
771 finally:
772 shutil.rmtree(TESTFN, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000773
Brian Curtin3b4499c2010-12-28 14:31:47 +0000774 @support.skip_unless_symlink
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000775 def test_dont_copy_file_onto_symlink_to_itself(self):
776 # bug 851123.
777 os.mkdir(TESTFN)
778 src = os.path.join(TESTFN, 'cheese')
779 dst = os.path.join(TESTFN, 'shop')
780 try:
781 with open(src, 'w') as f:
782 f.write('cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000783 # Using `src` here would mean we end up with a symlink pointing
784 # to TESTFN/TESTFN/cheese, while it should point at
785 # TESTFN/cheese.
786 os.symlink('cheese', dst)
Hynek Schlawack48653762012-10-07 12:49:58 +0200787 self.assertRaises(shutil.SameFileError, shutil.copyfile, src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000788 with open(src, 'r') as f:
789 self.assertEqual(f.read(), 'cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000790 os.remove(dst)
791 finally:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000792 shutil.rmtree(TESTFN, ignore_errors=True)
Johannes Gijsbers68128712004-08-14 13:57:08 +0000793
Brian Curtin3b4499c2010-12-28 14:31:47 +0000794 @support.skip_unless_symlink
Brian Curtind40e6f72010-07-08 21:39:08 +0000795 def test_rmtree_on_symlink(self):
796 # bug 1669.
797 os.mkdir(TESTFN)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000798 try:
Brian Curtind40e6f72010-07-08 21:39:08 +0000799 src = os.path.join(TESTFN, 'cheese')
800 dst = os.path.join(TESTFN, 'shop')
801 os.mkdir(src)
802 os.symlink(src, dst)
803 self.assertRaises(OSError, shutil.rmtree, dst)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200804 shutil.rmtree(dst, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000805 finally:
Brian Curtind40e6f72010-07-08 21:39:08 +0000806 shutil.rmtree(TESTFN, ignore_errors=True)
807
Serhiy Storchaka43767632013-11-03 21:31:38 +0200808 # Issue #3002: copyfile and copytree block indefinitely on named pipes
809 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
810 def test_copyfile_named_pipe(self):
811 os.mkfifo(TESTFN)
812 try:
813 self.assertRaises(shutil.SpecialFileError,
814 shutil.copyfile, TESTFN, TESTFN2)
815 self.assertRaises(shutil.SpecialFileError,
816 shutil.copyfile, __file__, TESTFN)
817 finally:
818 os.remove(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000819
Serhiy Storchaka43767632013-11-03 21:31:38 +0200820 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
821 @support.skip_unless_symlink
822 def test_copytree_named_pipe(self):
823 os.mkdir(TESTFN)
824 try:
825 subdir = os.path.join(TESTFN, "subdir")
826 os.mkdir(subdir)
827 pipe = os.path.join(subdir, "mypipe")
828 os.mkfifo(pipe)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000829 try:
Serhiy Storchaka43767632013-11-03 21:31:38 +0200830 shutil.copytree(TESTFN, TESTFN2)
831 except shutil.Error as e:
832 errors = e.args[0]
833 self.assertEqual(len(errors), 1)
834 src, dst, error_msg = errors[0]
835 self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
836 else:
837 self.fail("shutil.Error should have been raised")
838 finally:
839 shutil.rmtree(TESTFN, ignore_errors=True)
840 shutil.rmtree(TESTFN2, ignore_errors=True)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000841
Tarek Ziadé5340db32010-04-19 22:30:51 +0000842 def test_copytree_special_func(self):
843
844 src_dir = self.mkdtemp()
845 dst_dir = os.path.join(self.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200846 write_file((src_dir, 'test.txt'), '123')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000847 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200848 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000849
850 copied = []
851 def _copy(src, dst):
852 copied.append((src, dst))
853
854 shutil.copytree(src_dir, dst_dir, copy_function=_copy)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000855 self.assertEqual(len(copied), 2)
Tarek Ziadé5340db32010-04-19 22:30:51 +0000856
Brian Curtin3b4499c2010-12-28 14:31:47 +0000857 @support.skip_unless_symlink
Tarek Ziadéfb437512010-04-20 08:57:33 +0000858 def test_copytree_dangling_symlinks(self):
859
860 # a dangling symlink raises an error at the end
861 src_dir = self.mkdtemp()
862 dst_dir = os.path.join(self.mkdtemp(), 'destination')
863 os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt'))
864 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200865 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadéfb437512010-04-20 08:57:33 +0000866 self.assertRaises(Error, shutil.copytree, src_dir, dst_dir)
867
868 # a dangling symlink is ignored with the proper flag
869 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
870 shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)
871 self.assertNotIn('test.txt', os.listdir(dst_dir))
872
873 # a dangling symlink is copied if symlinks=True
874 dst_dir = os.path.join(self.mkdtemp(), 'destination3')
875 shutil.copytree(src_dir, dst_dir, symlinks=True)
876 self.assertIn('test.txt', os.listdir(dst_dir))
877
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400878 def _copy_file(self, method):
879 fname = 'test.txt'
880 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200881 write_file((tmpdir, fname), 'xxx')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400882 file1 = os.path.join(tmpdir, fname)
883 tmpdir2 = self.mkdtemp()
884 method(file1, tmpdir2)
885 file2 = os.path.join(tmpdir2, fname)
886 return (file1, file2)
887
888 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
889 def test_copy(self):
890 # Ensure that the copied file exists and has the same mode bits.
891 file1, file2 = self._copy_file(shutil.copy)
892 self.assertTrue(os.path.exists(file2))
893 self.assertEqual(os.stat(file1).st_mode, os.stat(file2).st_mode)
894
895 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
Senthil Kumaran0c2dba52011-07-03 18:21:38 -0700896 @unittest.skipUnless(hasattr(os, 'utime'), 'requires os.utime')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400897 def test_copy2(self):
898 # Ensure that the copied file exists and has the same mode and
899 # modification time bits.
900 file1, file2 = self._copy_file(shutil.copy2)
901 self.assertTrue(os.path.exists(file2))
902 file1_stat = os.stat(file1)
903 file2_stat = os.stat(file2)
904 self.assertEqual(file1_stat.st_mode, file2_stat.st_mode)
905 for attr in 'st_atime', 'st_mtime':
906 # The modification times may be truncated in the new file.
907 self.assertLessEqual(getattr(file1_stat, attr),
908 getattr(file2_stat, attr) + 1)
909 if hasattr(os, 'chflags') and hasattr(file1_stat, 'st_flags'):
910 self.assertEqual(getattr(file1_stat, 'st_flags'),
911 getattr(file2_stat, 'st_flags'))
912
Ezio Melotti975077a2011-05-19 22:03:22 +0300913 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000914 def test_make_tarball(self):
915 # creating something to tar
916 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200917 write_file((tmpdir, 'file1'), 'xxx')
918 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000919 os.mkdir(os.path.join(tmpdir, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200920 write_file((tmpdir, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000921
922 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400923 # force shutil to create the directory
924 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000925 unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
926 "source and target should be on same drive")
927
928 base_name = os.path.join(tmpdir2, 'archive')
929
930 # working with relative paths to avoid tar warnings
931 old_dir = os.getcwd()
932 os.chdir(tmpdir)
933 try:
934 _make_tarball(splitdrive(base_name)[1], '.')
935 finally:
936 os.chdir(old_dir)
937
938 # check if the compressed tarball was created
939 tarball = base_name + '.tar.gz'
940 self.assertTrue(os.path.exists(tarball))
941
942 # trying an uncompressed one
943 base_name = os.path.join(tmpdir2, 'archive')
944 old_dir = os.getcwd()
945 os.chdir(tmpdir)
946 try:
947 _make_tarball(splitdrive(base_name)[1], '.', compress=None)
948 finally:
949 os.chdir(old_dir)
950 tarball = base_name + '.tar'
951 self.assertTrue(os.path.exists(tarball))
952
953 def _tarinfo(self, path):
954 tar = tarfile.open(path)
955 try:
956 names = tar.getnames()
957 names.sort()
958 return tuple(names)
959 finally:
960 tar.close()
961
962 def _create_files(self):
963 # creating something to tar
964 tmpdir = self.mkdtemp()
965 dist = os.path.join(tmpdir, 'dist')
966 os.mkdir(dist)
Éric Araujoa7e33a12011-08-12 19:51:35 +0200967 write_file((dist, 'file1'), 'xxx')
968 write_file((dist, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000969 os.mkdir(os.path.join(dist, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200970 write_file((dist, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000971 os.mkdir(os.path.join(dist, 'sub2'))
972 tmpdir2 = self.mkdtemp()
973 base_name = os.path.join(tmpdir2, 'archive')
974 return tmpdir, tmpdir2, base_name
975
Ezio Melotti975077a2011-05-19 22:03:22 +0300976 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000977 @unittest.skipUnless(find_executable('tar') and find_executable('gzip'),
978 'Need the tar command to run')
979 def test_tarfile_vs_tar(self):
980 tmpdir, tmpdir2, base_name = self._create_files()
981 old_dir = os.getcwd()
982 os.chdir(tmpdir)
983 try:
984 _make_tarball(base_name, 'dist')
985 finally:
986 os.chdir(old_dir)
987
988 # check if the compressed tarball was created
989 tarball = base_name + '.tar.gz'
990 self.assertTrue(os.path.exists(tarball))
991
992 # now create another tarball using `tar`
993 tarball2 = os.path.join(tmpdir, 'archive2.tar.gz')
994 tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist']
995 gzip_cmd = ['gzip', '-f9', 'archive2.tar']
996 old_dir = os.getcwd()
997 os.chdir(tmpdir)
998 try:
999 with captured_stdout() as s:
1000 spawn(tar_cmd)
1001 spawn(gzip_cmd)
1002 finally:
1003 os.chdir(old_dir)
1004
1005 self.assertTrue(os.path.exists(tarball2))
1006 # let's compare both tarballs
Ezio Melottib3aedd42010-11-20 19:04:17 +00001007 self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001008
1009 # trying an uncompressed one
1010 base_name = os.path.join(tmpdir2, 'archive')
1011 old_dir = os.getcwd()
1012 os.chdir(tmpdir)
1013 try:
1014 _make_tarball(base_name, 'dist', compress=None)
1015 finally:
1016 os.chdir(old_dir)
1017 tarball = base_name + '.tar'
1018 self.assertTrue(os.path.exists(tarball))
1019
1020 # now for a dry_run
1021 base_name = os.path.join(tmpdir2, 'archive')
1022 old_dir = os.getcwd()
1023 os.chdir(tmpdir)
1024 try:
1025 _make_tarball(base_name, 'dist', compress=None, dry_run=True)
1026 finally:
1027 os.chdir(old_dir)
1028 tarball = base_name + '.tar'
1029 self.assertTrue(os.path.exists(tarball))
1030
Ezio Melotti975077a2011-05-19 22:03:22 +03001031 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001032 @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
1033 def test_make_zipfile(self):
1034 # creating something to tar
1035 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +02001036 write_file((tmpdir, 'file1'), 'xxx')
1037 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001038
1039 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001040 # force shutil to create the directory
1041 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001042 base_name = os.path.join(tmpdir2, 'archive')
1043 _make_zipfile(base_name, tmpdir)
1044
1045 # check if the compressed tarball was created
1046 tarball = base_name + '.zip'
Éric Araujo1c505492010-11-06 02:12:51 +00001047 self.assertTrue(os.path.exists(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001048
1049
1050 def test_make_archive(self):
1051 tmpdir = self.mkdtemp()
1052 base_name = os.path.join(tmpdir, 'archive')
1053 self.assertRaises(ValueError, make_archive, base_name, 'xxx')
1054
Ezio Melotti975077a2011-05-19 22:03:22 +03001055 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001056 def test_make_archive_owner_group(self):
1057 # testing make_archive with owner and group, with various combinations
1058 # this works even if there's not gid/uid support
1059 if UID_GID_SUPPORT:
1060 group = grp.getgrgid(0)[0]
1061 owner = pwd.getpwuid(0)[0]
1062 else:
1063 group = owner = 'root'
1064
1065 base_dir, root_dir, base_name = self._create_files()
1066 base_name = os.path.join(self.mkdtemp() , 'archive')
1067 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
1068 group=group)
1069 self.assertTrue(os.path.exists(res))
1070
1071 res = make_archive(base_name, 'zip', root_dir, base_dir)
1072 self.assertTrue(os.path.exists(res))
1073
1074 res = make_archive(base_name, 'tar', root_dir, base_dir,
1075 owner=owner, group=group)
1076 self.assertTrue(os.path.exists(res))
1077
1078 res = make_archive(base_name, 'tar', root_dir, base_dir,
1079 owner='kjhkjhkjg', group='oihohoh')
1080 self.assertTrue(os.path.exists(res))
1081
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001082
Ezio Melotti975077a2011-05-19 22:03:22 +03001083 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001084 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1085 def test_tarfile_root_owner(self):
1086 tmpdir, tmpdir2, base_name = self._create_files()
1087 old_dir = os.getcwd()
1088 os.chdir(tmpdir)
1089 group = grp.getgrgid(0)[0]
1090 owner = pwd.getpwuid(0)[0]
1091 try:
1092 archive_name = _make_tarball(base_name, 'dist', compress=None,
1093 owner=owner, group=group)
1094 finally:
1095 os.chdir(old_dir)
1096
1097 # check if the compressed tarball was created
1098 self.assertTrue(os.path.exists(archive_name))
1099
1100 # now checks the rights
1101 archive = tarfile.open(archive_name)
1102 try:
1103 for member in archive.getmembers():
Ezio Melottib3aedd42010-11-20 19:04:17 +00001104 self.assertEqual(member.uid, 0)
1105 self.assertEqual(member.gid, 0)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001106 finally:
1107 archive.close()
1108
1109 def test_make_archive_cwd(self):
1110 current_dir = os.getcwd()
1111 def _breaks(*args, **kw):
1112 raise RuntimeError()
1113
1114 register_archive_format('xxx', _breaks, [], 'xxx file')
1115 try:
1116 try:
1117 make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
1118 except Exception:
1119 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +00001120 self.assertEqual(os.getcwd(), current_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001121 finally:
1122 unregister_archive_format('xxx')
1123
1124 def test_register_archive_format(self):
1125
1126 self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
1127 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1128 1)
1129 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1130 [(1, 2), (1, 2, 3)])
1131
1132 register_archive_format('xxx', lambda: x, [(1, 2)], 'xxx file')
1133 formats = [name for name, params in get_archive_formats()]
1134 self.assertIn('xxx', formats)
1135
1136 unregister_archive_format('xxx')
1137 formats = [name for name, params in get_archive_formats()]
1138 self.assertNotIn('xxx', formats)
1139
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001140 def _compare_dirs(self, dir1, dir2):
1141 # check that dir1 and dir2 are equivalent,
1142 # return the diff
1143 diff = []
1144 for root, dirs, files in os.walk(dir1):
1145 for file_ in files:
1146 path = os.path.join(root, file_)
1147 target_path = os.path.join(dir2, os.path.split(path)[-1])
1148 if not os.path.exists(target_path):
1149 diff.append(file_)
1150 return diff
1151
Ezio Melotti975077a2011-05-19 22:03:22 +03001152 @requires_zlib
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001153 def test_unpack_archive(self):
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001154 formats = ['tar', 'gztar', 'zip']
1155 if BZ2_SUPPORTED:
1156 formats.append('bztar')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001157
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001158 for format in formats:
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001159 tmpdir = self.mkdtemp()
1160 base_dir, root_dir, base_name = self._create_files()
1161 tmpdir2 = self.mkdtemp()
1162 filename = make_archive(base_name, format, root_dir, base_dir)
1163
1164 # let's try to unpack it now
1165 unpack_archive(filename, tmpdir2)
1166 diff = self._compare_dirs(tmpdir, tmpdir2)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001167 self.assertEqual(diff, [])
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001168
Nick Coghlanabf202d2011-03-16 13:52:20 -04001169 # and again, this time with the format specified
1170 tmpdir3 = self.mkdtemp()
1171 unpack_archive(filename, tmpdir3, format=format)
1172 diff = self._compare_dirs(tmpdir, tmpdir3)
1173 self.assertEqual(diff, [])
1174 self.assertRaises(shutil.ReadError, unpack_archive, TESTFN)
1175 self.assertRaises(ValueError, unpack_archive, TESTFN, format='xxx')
1176
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001177 def test_unpack_registery(self):
1178
1179 formats = get_unpack_formats()
1180
1181 def _boo(filename, extract_dir, extra):
Ezio Melottib3aedd42010-11-20 19:04:17 +00001182 self.assertEqual(extra, 1)
1183 self.assertEqual(filename, 'stuff.boo')
1184 self.assertEqual(extract_dir, 'xx')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001185
1186 register_unpack_format('Boo', ['.boo', '.b2'], _boo, [('extra', 1)])
1187 unpack_archive('stuff.boo', 'xx')
1188
1189 # trying to register a .boo unpacker again
1190 self.assertRaises(RegistryError, register_unpack_format, 'Boo2',
1191 ['.boo'], _boo)
1192
1193 # should work now
1194 unregister_unpack_format('Boo')
1195 register_unpack_format('Boo2', ['.boo'], _boo)
1196 self.assertIn(('Boo2', ['.boo'], ''), get_unpack_formats())
1197 self.assertNotIn(('Boo', ['.boo'], ''), get_unpack_formats())
1198
1199 # let's leave a clean state
1200 unregister_unpack_format('Boo2')
Ezio Melottib3aedd42010-11-20 19:04:17 +00001201 self.assertEqual(get_unpack_formats(), formats)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001202
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001203 @unittest.skipUnless(hasattr(shutil, 'disk_usage'),
1204 "disk_usage not available on this platform")
1205 def test_disk_usage(self):
1206 usage = shutil.disk_usage(os.getcwd())
Éric Araujo2ee61882011-07-02 16:45:45 +02001207 self.assertGreater(usage.total, 0)
1208 self.assertGreater(usage.used, 0)
1209 self.assertGreaterEqual(usage.free, 0)
1210 self.assertGreaterEqual(usage.total, usage.used)
1211 self.assertGreater(usage.total, usage.free)
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001212
Sandro Tosid902a142011-08-22 23:28:27 +02001213 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1214 @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
1215 def test_chown(self):
1216
1217 # cleaned-up automatically by TestShutil.tearDown method
1218 dirname = self.mkdtemp()
1219 filename = tempfile.mktemp(dir=dirname)
1220 write_file(filename, 'testing chown function')
1221
1222 with self.assertRaises(ValueError):
1223 shutil.chown(filename)
1224
1225 with self.assertRaises(LookupError):
1226 shutil.chown(filename, user='non-exising username')
1227
1228 with self.assertRaises(LookupError):
1229 shutil.chown(filename, group='non-exising groupname')
1230
1231 with self.assertRaises(TypeError):
1232 shutil.chown(filename, b'spam')
1233
1234 with self.assertRaises(TypeError):
1235 shutil.chown(filename, 3.14)
1236
1237 uid = os.getuid()
1238 gid = os.getgid()
1239
1240 def check_chown(path, uid=None, gid=None):
1241 s = os.stat(filename)
1242 if uid is not None:
1243 self.assertEqual(uid, s.st_uid)
1244 if gid is not None:
1245 self.assertEqual(gid, s.st_gid)
1246
1247 shutil.chown(filename, uid, gid)
1248 check_chown(filename, uid, gid)
1249 shutil.chown(filename, uid)
1250 check_chown(filename, uid)
1251 shutil.chown(filename, user=uid)
1252 check_chown(filename, uid)
1253 shutil.chown(filename, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001254 check_chown(filename, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001255
1256 shutil.chown(dirname, uid, gid)
1257 check_chown(dirname, uid, gid)
1258 shutil.chown(dirname, uid)
1259 check_chown(dirname, uid)
1260 shutil.chown(dirname, user=uid)
1261 check_chown(dirname, uid)
1262 shutil.chown(dirname, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001263 check_chown(dirname, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001264
1265 user = pwd.getpwuid(uid)[0]
1266 group = grp.getgrgid(gid)[0]
1267 shutil.chown(filename, user, group)
1268 check_chown(filename, uid, gid)
1269 shutil.chown(dirname, user, group)
1270 check_chown(dirname, uid, gid)
1271
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001272 def test_copy_return_value(self):
1273 # copy and copy2 both return their destination path.
1274 for fn in (shutil.copy, shutil.copy2):
1275 src_dir = self.mkdtemp()
1276 dst_dir = self.mkdtemp()
1277 src = os.path.join(src_dir, 'foo')
1278 write_file(src, 'foo')
1279 rv = fn(src, dst_dir)
1280 self.assertEqual(rv, os.path.join(dst_dir, 'foo'))
1281 rv = fn(src, os.path.join(dst_dir, 'bar'))
1282 self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
1283
1284 def test_copyfile_return_value(self):
1285 # copytree returns its destination path.
1286 src_dir = self.mkdtemp()
1287 dst_dir = self.mkdtemp()
1288 dst_file = os.path.join(dst_dir, 'bar')
1289 src_file = os.path.join(src_dir, 'foo')
1290 write_file(src_file, 'foo')
1291 rv = shutil.copyfile(src_file, dst_file)
1292 self.assertTrue(os.path.exists(rv))
1293 self.assertEqual(read_file(src_file), read_file(dst_file))
1294
Hynek Schlawack48653762012-10-07 12:49:58 +02001295 def test_copyfile_same_file(self):
1296 # copyfile() should raise SameFileError if the source and destination
1297 # are the same.
1298 src_dir = self.mkdtemp()
1299 src_file = os.path.join(src_dir, 'foo')
1300 write_file(src_file, 'foo')
1301 self.assertRaises(SameFileError, shutil.copyfile, src_file, src_file)
Hynek Schlawack27ddb572012-10-28 13:59:27 +01001302 # But Error should work too, to stay backward compatible.
1303 self.assertRaises(Error, shutil.copyfile, src_file, src_file)
Hynek Schlawack48653762012-10-07 12:49:58 +02001304
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001305 def test_copytree_return_value(self):
1306 # copytree returns its destination path.
1307 src_dir = self.mkdtemp()
1308 dst_dir = src_dir + "dest"
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001309 self.addCleanup(shutil.rmtree, dst_dir, True)
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001310 src = os.path.join(src_dir, 'foo')
1311 write_file(src, 'foo')
1312 rv = shutil.copytree(src_dir, dst_dir)
1313 self.assertEqual(['foo'], os.listdir(rv))
1314
Christian Heimes9bd667a2008-01-20 15:14:11 +00001315
Brian Curtinc57a3452012-06-22 16:00:30 -05001316class TestWhich(unittest.TestCase):
1317
1318 def setUp(self):
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001319 self.temp_dir = tempfile.mkdtemp(prefix="Tmp")
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001320 self.addCleanup(shutil.rmtree, self.temp_dir, True)
Brian Curtinc57a3452012-06-22 16:00:30 -05001321 # Give the temp_file an ".exe" suffix for all.
1322 # It's needed on Windows and not harmful on other platforms.
1323 self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001324 prefix="Tmp",
1325 suffix=".Exe")
Brian Curtinc57a3452012-06-22 16:00:30 -05001326 os.chmod(self.temp_file.name, stat.S_IXUSR)
1327 self.addCleanup(self.temp_file.close)
1328 self.dir, self.file = os.path.split(self.temp_file.name)
1329
1330 def test_basic(self):
1331 # Given an EXE in a directory, it should be returned.
1332 rv = shutil.which(self.file, path=self.dir)
1333 self.assertEqual(rv, self.temp_file.name)
1334
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001335 def test_absolute_cmd(self):
Brian Curtinc57a3452012-06-22 16:00:30 -05001336 # When given the fully qualified path to an executable that exists,
1337 # it should be returned.
1338 rv = shutil.which(self.temp_file.name, path=self.temp_dir)
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001339 self.assertEqual(rv, self.temp_file.name)
1340
1341 def test_relative_cmd(self):
1342 # When given the relative path with a directory part to an executable
1343 # that exists, it should be returned.
1344 base_dir, tail_dir = os.path.split(self.dir)
1345 relpath = os.path.join(tail_dir, self.file)
Nick Coghlan55175962013-07-28 22:11:50 +10001346 with support.change_cwd(path=base_dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001347 rv = shutil.which(relpath, path=self.temp_dir)
1348 self.assertEqual(rv, relpath)
1349 # But it shouldn't be searched in PATH directories (issue #16957).
Nick Coghlan55175962013-07-28 22:11:50 +10001350 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001351 rv = shutil.which(relpath, path=base_dir)
1352 self.assertIsNone(rv)
1353
1354 def test_cwd(self):
1355 # Issue #16957
1356 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001357 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001358 rv = shutil.which(self.file, path=base_dir)
1359 if sys.platform == "win32":
1360 # Windows: current directory implicitly on PATH
1361 self.assertEqual(rv, os.path.join(os.curdir, self.file))
1362 else:
1363 # Other platforms: shouldn't match in the current directory.
1364 self.assertIsNone(rv)
Brian Curtinc57a3452012-06-22 16:00:30 -05001365
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001366 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
1367 'non-root user required')
Brian Curtinc57a3452012-06-22 16:00:30 -05001368 def test_non_matching_mode(self):
1369 # Set the file read-only and ask for writeable files.
1370 os.chmod(self.temp_file.name, stat.S_IREAD)
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001371 if os.access(self.temp_file.name, os.W_OK):
1372 self.skipTest("can't set the file read-only")
Brian Curtinc57a3452012-06-22 16:00:30 -05001373 rv = shutil.which(self.file, path=self.dir, mode=os.W_OK)
1374 self.assertIsNone(rv)
1375
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001376 def test_relative_path(self):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001377 base_dir, tail_dir = os.path.split(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001378 with support.change_cwd(path=base_dir):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001379 rv = shutil.which(self.file, path=tail_dir)
1380 self.assertEqual(rv, os.path.join(tail_dir, self.file))
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001381
Brian Curtinc57a3452012-06-22 16:00:30 -05001382 def test_nonexistent_file(self):
1383 # Return None when no matching executable file is found on the path.
1384 rv = shutil.which("foo.exe", path=self.dir)
1385 self.assertIsNone(rv)
1386
1387 @unittest.skipUnless(sys.platform == "win32",
1388 "pathext check is Windows-only")
1389 def test_pathext_checking(self):
1390 # Ask for the file without the ".exe" extension, then ensure that
1391 # it gets found properly with the extension.
Serhiy Storchakad70127a2013-01-24 20:03:49 +02001392 rv = shutil.which(self.file[:-4], path=self.dir)
Serhiy Storchaka80c88f42013-01-22 10:31:36 +02001393 self.assertEqual(rv, self.temp_file.name[:-4] + ".EXE")
Brian Curtinc57a3452012-06-22 16:00:30 -05001394
Barry Warsaw618738b2013-04-16 11:05:03 -04001395 def test_environ_path(self):
1396 with support.EnvironmentVarGuard() as env:
1397 env['PATH'] = self.dir
1398 rv = shutil.which(self.file)
1399 self.assertEqual(rv, self.temp_file.name)
1400
1401 def test_empty_path(self):
1402 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001403 with support.change_cwd(path=self.dir), \
Barry Warsaw618738b2013-04-16 11:05:03 -04001404 support.EnvironmentVarGuard() as env:
1405 env['PATH'] = self.dir
1406 rv = shutil.which(self.file, path='')
1407 self.assertIsNone(rv)
1408
1409 def test_empty_path_no_PATH(self):
1410 with support.EnvironmentVarGuard() as env:
1411 env.pop('PATH', None)
1412 rv = shutil.which(self.file)
1413 self.assertIsNone(rv)
1414
Brian Curtinc57a3452012-06-22 16:00:30 -05001415
Christian Heimesada8c3b2008-03-18 18:26:33 +00001416class TestMove(unittest.TestCase):
1417
1418 def setUp(self):
1419 filename = "foo"
1420 self.src_dir = tempfile.mkdtemp()
1421 self.dst_dir = tempfile.mkdtemp()
1422 self.src_file = os.path.join(self.src_dir, filename)
1423 self.dst_file = os.path.join(self.dst_dir, filename)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001424 with open(self.src_file, "wb") as f:
1425 f.write(b"spam")
1426
1427 def tearDown(self):
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001428 for d in (self.src_dir, self.dst_dir):
Christian Heimesada8c3b2008-03-18 18:26:33 +00001429 try:
1430 if d:
1431 shutil.rmtree(d)
1432 except:
1433 pass
1434
1435 def _check_move_file(self, src, dst, real_dst):
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001436 with open(src, "rb") as f:
1437 contents = f.read()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001438 shutil.move(src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001439 with open(real_dst, "rb") as f:
1440 self.assertEqual(contents, f.read())
Christian Heimesada8c3b2008-03-18 18:26:33 +00001441 self.assertFalse(os.path.exists(src))
1442
1443 def _check_move_dir(self, src, dst, real_dst):
1444 contents = sorted(os.listdir(src))
1445 shutil.move(src, dst)
1446 self.assertEqual(contents, sorted(os.listdir(real_dst)))
1447 self.assertFalse(os.path.exists(src))
1448
1449 def test_move_file(self):
1450 # Move a file to another location on the same filesystem.
1451 self._check_move_file(self.src_file, self.dst_file, self.dst_file)
1452
1453 def test_move_file_to_dir(self):
1454 # Move a file inside an existing dir on the same filesystem.
1455 self._check_move_file(self.src_file, self.dst_dir, self.dst_file)
1456
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001457 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001458 def test_move_file_other_fs(self):
1459 # Move a file to an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001460 self.test_move_file()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001461
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001462 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001463 def test_move_file_to_dir_other_fs(self):
1464 # Move a file to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001465 self.test_move_file_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001466
1467 def test_move_dir(self):
1468 # Move a dir to another location on the same filesystem.
1469 dst_dir = tempfile.mktemp()
1470 try:
1471 self._check_move_dir(self.src_dir, dst_dir, dst_dir)
1472 finally:
1473 try:
1474 shutil.rmtree(dst_dir)
1475 except:
1476 pass
1477
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001478 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001479 def test_move_dir_other_fs(self):
1480 # Move a dir to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001481 self.test_move_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001482
1483 def test_move_dir_to_dir(self):
1484 # Move a dir inside an existing dir on the same filesystem.
1485 self._check_move_dir(self.src_dir, self.dst_dir,
1486 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1487
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001488 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001489 def test_move_dir_to_dir_other_fs(self):
1490 # Move a dir inside an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001491 self.test_move_dir_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001492
1493 def test_existing_file_inside_dest_dir(self):
1494 # A file with the same name inside the destination dir already exists.
1495 with open(self.dst_file, "wb"):
1496 pass
1497 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir)
1498
1499 def test_dont_move_dir_in_itself(self):
1500 # Moving a dir inside itself raises an Error.
1501 dst = os.path.join(self.src_dir, "bar")
1502 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst)
1503
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001504 def test_destinsrc_false_negative(self):
1505 os.mkdir(TESTFN)
1506 try:
1507 for src, dst in [('srcdir', 'srcdir/dest')]:
1508 src = os.path.join(TESTFN, src)
1509 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001510 self.assertTrue(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001511 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001512 'dst (%s) is not in src (%s)' % (dst, src))
1513 finally:
1514 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001515
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001516 def test_destinsrc_false_positive(self):
1517 os.mkdir(TESTFN)
1518 try:
1519 for src, dst in [('srcdir', 'src/dest'), ('srcdir', 'srcdir.new')]:
1520 src = os.path.join(TESTFN, src)
1521 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001522 self.assertFalse(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001523 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001524 'dst (%s) is in src (%s)' % (dst, src))
1525 finally:
1526 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimes9bd667a2008-01-20 15:14:11 +00001527
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001528 @support.skip_unless_symlink
1529 @mock_rename
1530 def test_move_file_symlink(self):
1531 dst = os.path.join(self.src_dir, 'bar')
1532 os.symlink(self.src_file, dst)
1533 shutil.move(dst, self.dst_file)
1534 self.assertTrue(os.path.islink(self.dst_file))
1535 self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
1536
1537 @support.skip_unless_symlink
1538 @mock_rename
1539 def test_move_file_symlink_to_dir(self):
1540 filename = "bar"
1541 dst = os.path.join(self.src_dir, filename)
1542 os.symlink(self.src_file, dst)
1543 shutil.move(dst, self.dst_dir)
1544 final_link = os.path.join(self.dst_dir, filename)
1545 self.assertTrue(os.path.islink(final_link))
1546 self.assertTrue(os.path.samefile(self.src_file, final_link))
1547
1548 @support.skip_unless_symlink
1549 @mock_rename
1550 def test_move_dangling_symlink(self):
1551 src = os.path.join(self.src_dir, 'baz')
1552 dst = os.path.join(self.src_dir, 'bar')
1553 os.symlink(src, dst)
1554 dst_link = os.path.join(self.dst_dir, 'quux')
1555 shutil.move(dst, dst_link)
1556 self.assertTrue(os.path.islink(dst_link))
1557 self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
1558
1559 @support.skip_unless_symlink
1560 @mock_rename
1561 def test_move_dir_symlink(self):
1562 src = os.path.join(self.src_dir, 'baz')
1563 dst = os.path.join(self.src_dir, 'bar')
1564 os.mkdir(src)
1565 os.symlink(src, dst)
1566 dst_link = os.path.join(self.dst_dir, 'quux')
1567 shutil.move(dst, dst_link)
1568 self.assertTrue(os.path.islink(dst_link))
1569 self.assertTrue(os.path.samefile(src, dst_link))
1570
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001571 def test_move_return_value(self):
1572 rv = shutil.move(self.src_file, self.dst_dir)
1573 self.assertEqual(rv,
1574 os.path.join(self.dst_dir, os.path.basename(self.src_file)))
1575
1576 def test_move_as_rename_return_value(self):
1577 rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar'))
1578 self.assertEqual(rv, os.path.join(self.dst_dir, 'bar'))
1579
Tarek Ziadé5340db32010-04-19 22:30:51 +00001580
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001581class TestCopyFile(unittest.TestCase):
1582
1583 _delete = False
1584
1585 class Faux(object):
1586 _entered = False
1587 _exited_with = None
1588 _raised = False
1589 def __init__(self, raise_in_exit=False, suppress_at_exit=True):
1590 self._raise_in_exit = raise_in_exit
1591 self._suppress_at_exit = suppress_at_exit
1592 def read(self, *args):
1593 return ''
1594 def __enter__(self):
1595 self._entered = True
1596 def __exit__(self, exc_type, exc_val, exc_tb):
1597 self._exited_with = exc_type, exc_val, exc_tb
1598 if self._raise_in_exit:
1599 self._raised = True
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001600 raise OSError("Cannot close")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001601 return self._suppress_at_exit
1602
1603 def tearDown(self):
1604 if self._delete:
1605 del shutil.open
1606
1607 def _set_shutil_open(self, func):
1608 shutil.open = func
1609 self._delete = True
1610
1611 def test_w_source_open_fails(self):
1612 def _open(filename, mode='r'):
1613 if filename == 'srcfile':
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001614 raise OSError('Cannot open "srcfile"')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001615 assert 0 # shouldn't reach here.
1616
1617 self._set_shutil_open(_open)
1618
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001619 self.assertRaises(OSError, shutil.copyfile, 'srcfile', 'destfile')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001620
1621 def test_w_dest_open_fails(self):
1622
1623 srcfile = self.Faux()
1624
1625 def _open(filename, mode='r'):
1626 if filename == 'srcfile':
1627 return srcfile
1628 if filename == 'destfile':
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001629 raise OSError('Cannot open "destfile"')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001630 assert 0 # shouldn't reach here.
1631
1632 self._set_shutil_open(_open)
1633
1634 shutil.copyfile('srcfile', 'destfile')
1635 self.assertTrue(srcfile._entered)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001636 self.assertTrue(srcfile._exited_with[0] is OSError)
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001637 self.assertEqual(srcfile._exited_with[1].args,
1638 ('Cannot open "destfile"',))
1639
1640 def test_w_dest_close_fails(self):
1641
1642 srcfile = self.Faux()
1643 destfile = self.Faux(True)
1644
1645 def _open(filename, mode='r'):
1646 if filename == 'srcfile':
1647 return srcfile
1648 if filename == 'destfile':
1649 return destfile
1650 assert 0 # shouldn't reach here.
1651
1652 self._set_shutil_open(_open)
1653
1654 shutil.copyfile('srcfile', 'destfile')
1655 self.assertTrue(srcfile._entered)
1656 self.assertTrue(destfile._entered)
1657 self.assertTrue(destfile._raised)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001658 self.assertTrue(srcfile._exited_with[0] is OSError)
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001659 self.assertEqual(srcfile._exited_with[1].args,
1660 ('Cannot close',))
1661
1662 def test_w_source_close_fails(self):
1663
1664 srcfile = self.Faux(True)
1665 destfile = self.Faux()
1666
1667 def _open(filename, mode='r'):
1668 if filename == 'srcfile':
1669 return srcfile
1670 if filename == 'destfile':
1671 return destfile
1672 assert 0 # shouldn't reach here.
1673
1674 self._set_shutil_open(_open)
1675
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001676 self.assertRaises(OSError,
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001677 shutil.copyfile, 'srcfile', 'destfile')
1678 self.assertTrue(srcfile._entered)
1679 self.assertTrue(destfile._entered)
1680 self.assertFalse(destfile._raised)
1681 self.assertTrue(srcfile._exited_with[0] is None)
1682 self.assertTrue(srcfile._raised)
1683
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001684 def test_move_dir_caseinsensitive(self):
1685 # Renames a folder to the same name
1686 # but a different case.
1687
1688 self.src_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001689 self.addCleanup(shutil.rmtree, self.src_dir, True)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001690 dst_dir = os.path.join(
1691 os.path.dirname(self.src_dir),
1692 os.path.basename(self.src_dir).upper())
1693 self.assertNotEqual(self.src_dir, dst_dir)
1694
1695 try:
1696 shutil.move(self.src_dir, dst_dir)
1697 self.assertTrue(os.path.isdir(dst_dir))
1698 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +02001699 os.rmdir(dst_dir)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001700
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001701class TermsizeTests(unittest.TestCase):
1702 def test_does_not_crash(self):
1703 """Check if get_terminal_size() returns a meaningful value.
1704
1705 There's no easy portable way to actually check the size of the
1706 terminal, so let's check if it returns something sensible instead.
1707 """
1708 size = shutil.get_terminal_size()
Antoine Pitroucfade362012-02-08 23:48:59 +01001709 self.assertGreaterEqual(size.columns, 0)
1710 self.assertGreaterEqual(size.lines, 0)
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001711
1712 def test_os_environ_first(self):
1713 "Check if environment variables have precedence"
1714
1715 with support.EnvironmentVarGuard() as env:
1716 env['COLUMNS'] = '777'
1717 size = shutil.get_terminal_size()
1718 self.assertEqual(size.columns, 777)
1719
1720 with support.EnvironmentVarGuard() as env:
1721 env['LINES'] = '888'
1722 size = shutil.get_terminal_size()
1723 self.assertEqual(size.lines, 888)
1724
1725 @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
1726 def test_stty_match(self):
1727 """Check if stty returns the same results ignoring env
1728
1729 This test will fail if stdin and stdout are connected to
1730 different terminals with different sizes. Nevertheless, such
1731 situations should be pretty rare.
1732 """
1733 try:
1734 size = subprocess.check_output(['stty', 'size']).decode().split()
1735 except (FileNotFoundError, subprocess.CalledProcessError):
1736 self.skipTest("stty invocation failed")
1737 expected = (int(size[1]), int(size[0])) # reversed order
1738
1739 with support.EnvironmentVarGuard() as env:
1740 del env['LINES']
1741 del env['COLUMNS']
1742 actual = shutil.get_terminal_size()
1743
1744 self.assertEqual(expected, actual)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001745
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001746
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001747if __name__ == '__main__':
Brett Cannon3e9a9ae2013-06-12 21:25:59 -04001748 unittest.main()