blob: b98e7dc798abe79017202edc9b5131cb09caaca2 [file] [log] [blame]
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001# Copyright (C) 2003 Python Software Foundation
2
3import unittest
Berker Peksag884afd92014-12-10 02:50:32 +02004import unittest.mock
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00005import shutil
6import tempfile
Johannes Gijsbers8e6f2de2004-11-23 09:27:27 +00007import sys
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +00008import stat
Brett Cannon1c3fa182004-06-19 21:11:35 +00009import os
10import os.path
Antoine Pitrouc041ab62012-01-02 19:18:02 +010011import errno
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040012import functools
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -070013import pathlib
Antoine Pitroubcf2b592012-02-08 23:28:36 +010014import subprocess
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020015import random
16import string
17import contextlib
18import io
Serhiy Storchaka527ef072015-09-06 18:33:19 +030019from shutil import (make_archive,
Tarek Ziadé396fad72010-02-23 05:30:31 +000020 register_archive_format, unregister_archive_format,
Tarek Ziadé6ac91722010-04-28 17:51:36 +000021 get_archive_formats, Error, unpack_archive,
22 register_unpack_format, RegistryError,
Hynek Schlawack48653762012-10-07 12:49:58 +020023 unregister_unpack_format, get_unpack_formats,
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020024 SameFileError, _GiveupOnFastCopy)
Tarek Ziadé396fad72010-02-23 05:30:31 +000025import tarfile
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +020026import zipfile
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020027try:
28 import posix
29except ImportError:
30 posix = None
Tarek Ziadé396fad72010-02-23 05:30:31 +000031
32from test import support
Serhiy Storchakab21d1552018-03-02 11:53:51 +020033from test.support import TESTFN, FakePath
Serhiy Storchaka11213772014-08-06 18:50:19 +030034
Antoine Pitrou7fff0962009-05-01 21:09:44 +000035TESTFN2 = TESTFN + "2"
Victor Stinner937ee9e2018-06-26 02:11:06 +020036MACOS = sys.platform.startswith("darwin")
Michael Feltef110b12019-02-18 12:02:44 +010037AIX = sys.platform[:3] == 'aix'
Tarek Ziadé396fad72010-02-23 05:30:31 +000038try:
39 import grp
40 import pwd
41 UID_GID_SUPPORT = True
42except ImportError:
43 UID_GID_SUPPORT = False
44
Steve Dower9eb3d542019-08-21 15:52:42 -070045try:
46 import _winapi
47except ImportError:
48 _winapi = None
49
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040050def _fake_rename(*args, **kwargs):
51 # Pretend the destination path is on a different filesystem.
Antoine Pitrouc041ab62012-01-02 19:18:02 +010052 raise OSError(getattr(errno, 'EXDEV', 18), "Invalid cross-device link")
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040053
54def mock_rename(func):
55 @functools.wraps(func)
56 def wrap(*args, **kwargs):
57 try:
58 builtin_rename = os.rename
59 os.rename = _fake_rename
60 return func(*args, **kwargs)
61 finally:
62 os.rename = builtin_rename
63 return wrap
64
Éric Araujoa7e33a12011-08-12 19:51:35 +020065def write_file(path, content, binary=False):
66 """Write *content* to a file located at *path*.
67
68 If *path* is a tuple instead of a string, os.path.join will be used to
69 make a path. If *binary* is true, the file will be opened in binary
70 mode.
71 """
72 if isinstance(path, tuple):
73 path = os.path.join(*path)
74 with open(path, 'wb' if binary else 'w') as fp:
75 fp.write(content)
76
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +020077def write_test_file(path, size):
78 """Create a test file with an arbitrary size and random text content."""
79 def chunks(total, step):
80 assert total >= step
81 while total > step:
82 yield step
83 total -= step
84 if total:
85 yield total
86
87 bufsize = min(size, 8192)
88 chunk = b"".join([random.choice(string.ascii_letters).encode()
89 for i in range(bufsize)])
90 with open(path, 'wb') as f:
91 for csize in chunks(size, bufsize):
92 f.write(chunk)
93 assert os.path.getsize(path) == size
94
Éric Araujoa7e33a12011-08-12 19:51:35 +020095def read_file(path, binary=False):
96 """Return contents from a file located at *path*.
97
98 If *path* is a tuple instead of a string, os.path.join will be used to
99 make a path. If *binary* is true, the file will be opened in binary
100 mode.
101 """
102 if isinstance(path, tuple):
103 path = os.path.join(*path)
104 with open(path, 'rb' if binary else 'r') as fp:
105 return fp.read()
106
Serhiy Storchaka527ef072015-09-06 18:33:19 +0300107def rlistdir(path):
108 res = []
109 for name in sorted(os.listdir(path)):
110 p = os.path.join(path, name)
111 if os.path.isdir(p) and not os.path.islink(p):
112 res.append(name + '/')
113 for n in rlistdir(p):
114 res.append(name + '/' + n)
115 else:
116 res.append(name)
117 return res
118
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +0200119def supports_file2file_sendfile():
120 # ...apparently Linux and Solaris are the only ones
121 if not hasattr(os, "sendfile"):
122 return False
123 srcname = None
124 dstname = None
125 try:
126 with tempfile.NamedTemporaryFile("wb", delete=False) as f:
127 srcname = f.name
128 f.write(b"0123456789")
129
130 with open(srcname, "rb") as src:
131 with tempfile.NamedTemporaryFile("wb", delete=False) as dst:
Miss Islington (bot)7fe81ce2019-06-26 16:57:49 -0700132 dstname = dst.name
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +0200133 infd = src.fileno()
134 outfd = dst.fileno()
135 try:
136 os.sendfile(outfd, infd, 0, 2)
137 except OSError:
138 return False
139 else:
140 return True
141 finally:
142 if srcname is not None:
143 support.unlink(srcname)
144 if dstname is not None:
145 support.unlink(dstname)
146
147
148SUPPORTS_SENDFILE = supports_file2file_sendfile()
149
Michael Feltef110b12019-02-18 12:02:44 +0100150# AIX 32-bit mode, by default, lacks enough memory for the xz/lzma compiler test
151# The AIX command 'dump -o program' gives XCOFF header information
152# The second word of the last line in the maxdata value
153# when 32-bit maxdata must be greater than 0x1000000 for the xz test to succeed
154def _maxdataOK():
155 if AIX and sys.maxsize == 2147483647:
156 hdrs=subprocess.getoutput("/usr/bin/dump -o %s" % sys.executable)
157 maxdata=hdrs.split("\n")[-1].split()[1]
158 return int(maxdata,16) >= 0x20000000
159 else:
160 return True
Éric Araujoa7e33a12011-08-12 19:51:35 +0200161
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000162class TestShutil(unittest.TestCase):
Tarek Ziadé396fad72010-02-23 05:30:31 +0000163
164 def setUp(self):
165 super(TestShutil, self).setUp()
166 self.tempdirs = []
167
168 def tearDown(self):
169 super(TestShutil, self).tearDown()
170 while self.tempdirs:
171 d = self.tempdirs.pop()
172 shutil.rmtree(d, os.name in ('nt', 'cygwin'))
173
Tarek Ziadé396fad72010-02-23 05:30:31 +0000174
175 def mkdtemp(self):
176 """Create a temporary directory that will be cleaned up.
177
178 Returns the path of the directory.
179 """
Steve Dower66c0f012019-11-15 15:25:03 -0800180 basedir = None
181 if sys.platform == "win32":
182 basedir = os.path.realpath(os.getcwd())
183 d = tempfile.mkdtemp(dir=basedir)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000184 self.tempdirs.append(d)
185 return d
Tarek Ziadé5340db32010-04-19 22:30:51 +0000186
Hynek Schlawack3b527782012-06-25 13:27:31 +0200187 def test_rmtree_works_on_bytes(self):
188 tmp = self.mkdtemp()
189 victim = os.path.join(tmp, 'killme')
190 os.mkdir(victim)
191 write_file(os.path.join(victim, 'somefile'), 'foo')
192 victim = os.fsencode(victim)
193 self.assertIsInstance(victim, bytes)
Steve Dowere58571b2016-09-08 11:11:13 -0700194 shutil.rmtree(victim)
Hynek Schlawack3b527782012-06-25 13:27:31 +0200195
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200196 @support.skip_unless_symlink
197 def test_rmtree_fails_on_symlink(self):
198 tmp = self.mkdtemp()
199 dir_ = os.path.join(tmp, 'dir')
200 os.mkdir(dir_)
201 link = os.path.join(tmp, 'link')
202 os.symlink(dir_, link)
203 self.assertRaises(OSError, shutil.rmtree, link)
204 self.assertTrue(os.path.exists(dir_))
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100205 self.assertTrue(os.path.lexists(link))
206 errors = []
207 def onerror(*args):
208 errors.append(args)
209 shutil.rmtree(link, onerror=onerror)
210 self.assertEqual(len(errors), 1)
211 self.assertIs(errors[0][0], os.path.islink)
212 self.assertEqual(errors[0][1], link)
213 self.assertIsInstance(errors[0][2][1], OSError)
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200214
215 @support.skip_unless_symlink
216 def test_rmtree_works_on_symlinks(self):
217 tmp = self.mkdtemp()
218 dir1 = os.path.join(tmp, 'dir1')
219 dir2 = os.path.join(dir1, 'dir2')
220 dir3 = os.path.join(tmp, 'dir3')
221 for d in dir1, dir2, dir3:
222 os.mkdir(d)
223 file1 = os.path.join(tmp, 'file1')
224 write_file(file1, 'foo')
225 link1 = os.path.join(dir1, 'link1')
226 os.symlink(dir2, link1)
227 link2 = os.path.join(dir1, 'link2')
228 os.symlink(dir3, link2)
229 link3 = os.path.join(dir1, 'link3')
230 os.symlink(file1, link3)
231 # make sure symlinks are removed but not followed
232 shutil.rmtree(dir1)
233 self.assertFalse(os.path.exists(dir1))
234 self.assertTrue(os.path.exists(dir3))
235 self.assertTrue(os.path.exists(file1))
236
Steve Dower9eb3d542019-08-21 15:52:42 -0700237 @unittest.skipUnless(_winapi, 'only relevant on Windows')
238 def test_rmtree_fails_on_junctions(self):
239 tmp = self.mkdtemp()
240 dir_ = os.path.join(tmp, 'dir')
241 os.mkdir(dir_)
242 link = os.path.join(tmp, 'link')
243 _winapi.CreateJunction(dir_, link)
244 self.assertRaises(OSError, shutil.rmtree, link)
245 self.assertTrue(os.path.exists(dir_))
246 self.assertTrue(os.path.lexists(link))
247 errors = []
248 def onerror(*args):
249 errors.append(args)
250 shutil.rmtree(link, onerror=onerror)
251 self.assertEqual(len(errors), 1)
252 self.assertIs(errors[0][0], os.path.islink)
253 self.assertEqual(errors[0][1], link)
254 self.assertIsInstance(errors[0][2][1], OSError)
255
256 @unittest.skipUnless(_winapi, 'only relevant on Windows')
257 def test_rmtree_works_on_junctions(self):
258 tmp = self.mkdtemp()
259 dir1 = os.path.join(tmp, 'dir1')
260 dir2 = os.path.join(dir1, 'dir2')
261 dir3 = os.path.join(tmp, 'dir3')
262 for d in dir1, dir2, dir3:
263 os.mkdir(d)
264 file1 = os.path.join(tmp, 'file1')
265 write_file(file1, 'foo')
266 link1 = os.path.join(dir1, 'link1')
267 _winapi.CreateJunction(dir2, link1)
268 link2 = os.path.join(dir1, 'link2')
269 _winapi.CreateJunction(dir3, link2)
270 link3 = os.path.join(dir1, 'link3')
271 _winapi.CreateJunction(file1, link3)
272 # make sure junctions are removed but not followed
273 shutil.rmtree(dir1)
274 self.assertFalse(os.path.exists(dir1))
275 self.assertTrue(os.path.exists(dir3))
276 self.assertTrue(os.path.exists(file1))
277
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000278 def test_rmtree_errors(self):
279 # filename is guaranteed not to exist
280 filename = tempfile.mktemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100281 self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
282 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100283 shutil.rmtree(filename, ignore_errors=True)
284
285 # existing file
286 tmpdir = self.mkdtemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100287 write_file((tmpdir, "tstfile"), "")
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100288 filename = os.path.join(tmpdir, "tstfile")
Hynek Schlawackb5501102012-12-10 09:11:25 +0100289 with self.assertRaises(NotADirectoryError) as cm:
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100290 shutil.rmtree(filename)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100291 # The reason for this rather odd construct is that Windows sprinkles
292 # a \*.* at the end of file names. But only sometimes on some buildbots
293 possible_args = [filename, os.path.join(filename, '*.*')]
294 self.assertIn(cm.exception.filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100295 self.assertTrue(os.path.exists(filename))
Hynek Schlawackb5501102012-12-10 09:11:25 +0100296 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100297 shutil.rmtree(filename, ignore_errors=True)
298 self.assertTrue(os.path.exists(filename))
299 errors = []
300 def onerror(*args):
301 errors.append(args)
302 shutil.rmtree(filename, onerror=onerror)
303 self.assertEqual(len(errors), 2)
Serhiy Storchakad4d79bc2017-11-04 14:16:35 +0200304 self.assertIs(errors[0][0], os.scandir)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100305 self.assertEqual(errors[0][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100306 self.assertIsInstance(errors[0][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100307 self.assertIn(errors[0][2][1].filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100308 self.assertIs(errors[1][0], os.rmdir)
309 self.assertEqual(errors[1][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100310 self.assertIsInstance(errors[1][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100311 self.assertIn(errors[1][2][1].filename, possible_args)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000312
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000313
Serhiy Storchaka43767632013-11-03 21:31:38 +0200314 @unittest.skipIf(sys.platform[:6] == 'cygwin',
315 "This test can't be run on Cygwin (issue #1071513).")
316 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
317 "This test can't be run reliably as root (issue #1076467).")
318 def test_on_error(self):
319 self.errorState = 0
320 os.mkdir(TESTFN)
321 self.addCleanup(shutil.rmtree, TESTFN)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200322
Serhiy Storchaka43767632013-11-03 21:31:38 +0200323 self.child_file_path = os.path.join(TESTFN, 'a')
324 self.child_dir_path = os.path.join(TESTFN, 'b')
325 support.create_empty_file(self.child_file_path)
326 os.mkdir(self.child_dir_path)
327 old_dir_mode = os.stat(TESTFN).st_mode
328 old_child_file_mode = os.stat(self.child_file_path).st_mode
329 old_child_dir_mode = os.stat(self.child_dir_path).st_mode
330 # Make unwritable.
331 new_mode = stat.S_IREAD|stat.S_IEXEC
332 os.chmod(self.child_file_path, new_mode)
333 os.chmod(self.child_dir_path, new_mode)
334 os.chmod(TESTFN, new_mode)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000335
Serhiy Storchaka43767632013-11-03 21:31:38 +0200336 self.addCleanup(os.chmod, TESTFN, old_dir_mode)
337 self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
338 self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200339
Serhiy Storchaka43767632013-11-03 21:31:38 +0200340 shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
341 # Test whether onerror has actually been called.
342 self.assertEqual(self.errorState, 3,
343 "Expected call to onerror function did not happen.")
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000344
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000345 def check_args_to_onerror(self, func, arg, exc):
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000346 # test_rmtree_errors deliberately runs rmtree
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200347 # on a directory that is chmod 500, which will fail.
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000348 # This function is run when shutil.rmtree fails.
349 # 99.9% of the time it initially fails to remove
350 # a file in the directory, so the first time through
351 # func is os.remove.
352 # However, some Linux machines running ZFS on
353 # FUSE experienced a failure earlier in the process
354 # at os.listdir. The first failure may legally
355 # be either.
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200356 if self.errorState < 2:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200357 if func is os.unlink:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200358 self.assertEqual(arg, self.child_file_path)
359 elif func is os.rmdir:
360 self.assertEqual(arg, self.child_dir_path)
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000361 else:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200362 self.assertIs(func, os.listdir)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200363 self.assertIn(arg, [TESTFN, self.child_dir_path])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000364 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200365 self.errorState += 1
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000366 else:
367 self.assertEqual(func, os.rmdir)
368 self.assertEqual(arg, TESTFN)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000369 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200370 self.errorState = 3
371
372 def test_rmtree_does_not_choke_on_failing_lstat(self):
373 try:
374 orig_lstat = os.lstat
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200375 def raiser(fn, *args, **kwargs):
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200376 if fn != TESTFN:
377 raise OSError()
378 else:
379 return orig_lstat(fn)
380 os.lstat = raiser
381
382 os.mkdir(TESTFN)
383 write_file((TESTFN, 'foo'), 'foo')
384 shutil.rmtree(TESTFN)
385 finally:
386 os.lstat = orig_lstat
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000387
Antoine Pitrou78091e62011-12-29 18:54:15 +0100388 @support.skip_unless_symlink
389 def test_copymode_follow_symlinks(self):
390 tmp_dir = self.mkdtemp()
391 src = os.path.join(tmp_dir, 'foo')
392 dst = os.path.join(tmp_dir, 'bar')
393 src_link = os.path.join(tmp_dir, 'baz')
394 dst_link = os.path.join(tmp_dir, 'quux')
395 write_file(src, 'foo')
396 write_file(dst, 'foo')
397 os.symlink(src, src_link)
398 os.symlink(dst, dst_link)
399 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
400 # file to file
401 os.chmod(dst, stat.S_IRWXO)
402 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
403 shutil.copymode(src, dst)
404 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
Antoine Pitrou3f48ac92014-01-01 02:50:45 +0100405 # On Windows, os.chmod does not follow symlinks (issue #15411)
406 if os.name != 'nt':
407 # follow src link
408 os.chmod(dst, stat.S_IRWXO)
409 shutil.copymode(src_link, dst)
410 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
411 # follow dst link
412 os.chmod(dst, stat.S_IRWXO)
413 shutil.copymode(src, dst_link)
414 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
415 # follow both links
416 os.chmod(dst, stat.S_IRWXO)
417 shutil.copymode(src_link, dst_link)
418 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100419
420 @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
421 @support.skip_unless_symlink
422 def test_copymode_symlink_to_symlink(self):
423 tmp_dir = self.mkdtemp()
424 src = os.path.join(tmp_dir, 'foo')
425 dst = os.path.join(tmp_dir, 'bar')
426 src_link = os.path.join(tmp_dir, 'baz')
427 dst_link = os.path.join(tmp_dir, 'quux')
428 write_file(src, 'foo')
429 write_file(dst, 'foo')
430 os.symlink(src, src_link)
431 os.symlink(dst, dst_link)
432 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
433 os.chmod(dst, stat.S_IRWXU)
434 os.lchmod(src_link, stat.S_IRWXO|stat.S_IRWXG)
435 # link to link
436 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700437 shutil.copymode(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100438 self.assertEqual(os.lstat(src_link).st_mode,
439 os.lstat(dst_link).st_mode)
440 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
441 # src link - use chmod
442 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700443 shutil.copymode(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100444 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
445 # dst link - use chmod
446 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700447 shutil.copymode(src, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100448 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
449
450 @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing')
451 @support.skip_unless_symlink
452 def test_copymode_symlink_to_symlink_wo_lchmod(self):
453 tmp_dir = self.mkdtemp()
454 src = os.path.join(tmp_dir, 'foo')
455 dst = os.path.join(tmp_dir, 'bar')
456 src_link = os.path.join(tmp_dir, 'baz')
457 dst_link = os.path.join(tmp_dir, 'quux')
458 write_file(src, 'foo')
459 write_file(dst, 'foo')
460 os.symlink(src, src_link)
461 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700462 shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail
Antoine Pitrou78091e62011-12-29 18:54:15 +0100463
464 @support.skip_unless_symlink
465 def test_copystat_symlinks(self):
466 tmp_dir = self.mkdtemp()
467 src = os.path.join(tmp_dir, 'foo')
468 dst = os.path.join(tmp_dir, 'bar')
469 src_link = os.path.join(tmp_dir, 'baz')
470 dst_link = os.path.join(tmp_dir, 'qux')
471 write_file(src, 'foo')
472 src_stat = os.stat(src)
473 os.utime(src, (src_stat.st_atime,
474 src_stat.st_mtime - 42.0)) # ensure different mtimes
475 write_file(dst, 'bar')
476 self.assertNotEqual(os.stat(src).st_mtime, os.stat(dst).st_mtime)
477 os.symlink(src, src_link)
478 os.symlink(dst, dst_link)
479 if hasattr(os, 'lchmod'):
480 os.lchmod(src_link, stat.S_IRWXO)
481 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
482 os.lchflags(src_link, stat.UF_NODUMP)
483 src_link_stat = os.lstat(src_link)
484 # follow
485 if hasattr(os, 'lchmod'):
Larry Hastingsb4038062012-07-15 10:57:38 -0700486 shutil.copystat(src_link, dst_link, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100487 self.assertNotEqual(src_link_stat.st_mode, os.stat(dst).st_mode)
488 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700489 shutil.copystat(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100490 dst_link_stat = os.lstat(dst_link)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700491 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100492 for attr in 'st_atime', 'st_mtime':
493 # The modification times may be truncated in the new file.
494 self.assertLessEqual(getattr(src_link_stat, attr),
495 getattr(dst_link_stat, attr) + 1)
496 if hasattr(os, 'lchmod'):
497 self.assertEqual(src_link_stat.st_mode, dst_link_stat.st_mode)
498 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
499 self.assertEqual(src_link_stat.st_flags, dst_link_stat.st_flags)
500 # tell to follow but dst is not a link
Larry Hastingsb4038062012-07-15 10:57:38 -0700501 shutil.copystat(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100502 self.assertTrue(abs(os.stat(src).st_mtime - os.stat(dst).st_mtime) <
503 00000.1)
504
Ned Deilybaf75712012-05-10 17:05:19 -0700505 @unittest.skipUnless(hasattr(os, 'chflags') and
506 hasattr(errno, 'EOPNOTSUPP') and
507 hasattr(errno, 'ENOTSUP'),
508 "requires os.chflags, EOPNOTSUPP & ENOTSUP")
509 def test_copystat_handles_harmless_chflags_errors(self):
510 tmpdir = self.mkdtemp()
511 file1 = os.path.join(tmpdir, 'file1')
512 file2 = os.path.join(tmpdir, 'file2')
513 write_file(file1, 'xxx')
514 write_file(file2, 'xxx')
515
516 def make_chflags_raiser(err):
517 ex = OSError()
518
Larry Hastings90867a52012-06-22 17:01:41 -0700519 def _chflags_raiser(path, flags, *, follow_symlinks=True):
Ned Deilybaf75712012-05-10 17:05:19 -0700520 ex.errno = err
521 raise ex
522 return _chflags_raiser
523 old_chflags = os.chflags
524 try:
525 for err in errno.EOPNOTSUPP, errno.ENOTSUP:
526 os.chflags = make_chflags_raiser(err)
527 shutil.copystat(file1, file2)
528 # assert others errors break it
529 os.chflags = make_chflags_raiser(errno.EOPNOTSUPP + errno.ENOTSUP)
530 self.assertRaises(OSError, shutil.copystat, file1, file2)
531 finally:
532 os.chflags = old_chflags
533
Antoine Pitrou424246f2012-05-12 19:02:01 +0200534 @support.skip_unless_xattr
535 def test_copyxattr(self):
536 tmp_dir = self.mkdtemp()
537 src = os.path.join(tmp_dir, 'foo')
538 write_file(src, 'foo')
539 dst = os.path.join(tmp_dir, 'bar')
540 write_file(dst, 'bar')
541
542 # no xattr == no problem
543 shutil._copyxattr(src, dst)
544 # common case
545 os.setxattr(src, 'user.foo', b'42')
546 os.setxattr(src, 'user.bar', b'43')
547 shutil._copyxattr(src, dst)
Gregory P. Smith1093bf22014-01-17 12:01:22 -0800548 self.assertEqual(sorted(os.listxattr(src)), sorted(os.listxattr(dst)))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200549 self.assertEqual(
550 os.getxattr(src, 'user.foo'),
551 os.getxattr(dst, 'user.foo'))
552 # check errors don't affect other attrs
553 os.remove(dst)
554 write_file(dst, 'bar')
555 os_error = OSError(errno.EPERM, 'EPERM')
556
Larry Hastings9cf065c2012-06-22 16:30:09 -0700557 def _raise_on_user_foo(fname, attr, val, **kwargs):
Antoine Pitrou424246f2012-05-12 19:02:01 +0200558 if attr == 'user.foo':
559 raise os_error
560 else:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700561 orig_setxattr(fname, attr, val, **kwargs)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200562 try:
563 orig_setxattr = os.setxattr
564 os.setxattr = _raise_on_user_foo
565 shutil._copyxattr(src, dst)
Antoine Pitrou61597d32012-05-12 23:37:35 +0200566 self.assertIn('user.bar', os.listxattr(dst))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200567 finally:
568 os.setxattr = orig_setxattr
Hynek Schlawack0beab052013-02-05 08:22:44 +0100569 # the source filesystem not supporting xattrs should be ok, too.
570 def _raise_on_src(fname, *, follow_symlinks=True):
571 if fname == src:
572 raise OSError(errno.ENOTSUP, 'Operation not supported')
573 return orig_listxattr(fname, follow_symlinks=follow_symlinks)
574 try:
575 orig_listxattr = os.listxattr
576 os.listxattr = _raise_on_src
577 shutil._copyxattr(src, dst)
578 finally:
579 os.listxattr = orig_listxattr
Antoine Pitrou424246f2012-05-12 19:02:01 +0200580
Larry Hastingsad5ae042012-07-14 17:55:11 -0700581 # test that shutil.copystat copies xattrs
582 src = os.path.join(tmp_dir, 'the_original')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500583 srcro = os.path.join(tmp_dir, 'the_original_ro')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700584 write_file(src, src)
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500585 write_file(srcro, srcro)
Larry Hastingsad5ae042012-07-14 17:55:11 -0700586 os.setxattr(src, 'user.the_value', b'fiddly')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500587 os.setxattr(srcro, 'user.the_value', b'fiddly')
588 os.chmod(srcro, 0o444)
Larry Hastingsad5ae042012-07-14 17:55:11 -0700589 dst = os.path.join(tmp_dir, 'the_copy')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500590 dstro = os.path.join(tmp_dir, 'the_copy_ro')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700591 write_file(dst, dst)
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500592 write_file(dstro, dstro)
Larry Hastingsad5ae042012-07-14 17:55:11 -0700593 shutil.copystat(src, dst)
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500594 shutil.copystat(srcro, dstro)
Hynek Schlawackc2d481f2012-07-16 17:11:10 +0200595 self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly')
Olexa Bilaniuk79efbb72019-05-09 22:22:06 -0500596 self.assertEqual(os.getxattr(dstro, 'user.the_value'), b'fiddly')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700597
Antoine Pitrou424246f2012-05-12 19:02:01 +0200598 @support.skip_unless_symlink
599 @support.skip_unless_xattr
600 @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,
601 'root privileges required')
602 def test_copyxattr_symlinks(self):
603 # On Linux, it's only possible to access non-user xattr for symlinks;
604 # which in turn require root privileges. This test should be expanded
605 # as soon as other platforms gain support for extended attributes.
606 tmp_dir = self.mkdtemp()
607 src = os.path.join(tmp_dir, 'foo')
608 src_link = os.path.join(tmp_dir, 'baz')
609 write_file(src, 'foo')
610 os.symlink(src, src_link)
611 os.setxattr(src, 'trusted.foo', b'42')
Larry Hastings9cf065c2012-06-22 16:30:09 -0700612 os.setxattr(src_link, 'trusted.foo', b'43', follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200613 dst = os.path.join(tmp_dir, 'bar')
614 dst_link = os.path.join(tmp_dir, 'qux')
615 write_file(dst, 'bar')
616 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700617 shutil._copyxattr(src_link, dst_link, follow_symlinks=False)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700618 self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43')
Antoine Pitrou424246f2012-05-12 19:02:01 +0200619 self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo')
Larry Hastingsb4038062012-07-15 10:57:38 -0700620 shutil._copyxattr(src_link, dst, follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200621 self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43')
622
Antoine Pitrou78091e62011-12-29 18:54:15 +0100623 @support.skip_unless_symlink
624 def test_copy_symlinks(self):
625 tmp_dir = self.mkdtemp()
626 src = os.path.join(tmp_dir, 'foo')
627 dst = os.path.join(tmp_dir, 'bar')
628 src_link = os.path.join(tmp_dir, 'baz')
629 write_file(src, 'foo')
630 os.symlink(src, src_link)
631 if hasattr(os, 'lchmod'):
632 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
633 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700634 shutil.copy(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100635 self.assertFalse(os.path.islink(dst))
636 self.assertEqual(read_file(src), read_file(dst))
637 os.remove(dst)
638 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700639 shutil.copy(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100640 self.assertTrue(os.path.islink(dst))
641 self.assertEqual(os.readlink(dst), os.readlink(src_link))
642 if hasattr(os, 'lchmod'):
643 self.assertEqual(os.lstat(src_link).st_mode,
644 os.lstat(dst).st_mode)
645
646 @support.skip_unless_symlink
647 def test_copy2_symlinks(self):
648 tmp_dir = self.mkdtemp()
649 src = os.path.join(tmp_dir, 'foo')
650 dst = os.path.join(tmp_dir, 'bar')
651 src_link = os.path.join(tmp_dir, 'baz')
652 write_file(src, 'foo')
653 os.symlink(src, src_link)
654 if hasattr(os, 'lchmod'):
655 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
656 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
657 os.lchflags(src_link, stat.UF_NODUMP)
658 src_stat = os.stat(src)
659 src_link_stat = os.lstat(src_link)
660 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700661 shutil.copy2(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100662 self.assertFalse(os.path.islink(dst))
663 self.assertEqual(read_file(src), read_file(dst))
664 os.remove(dst)
665 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700666 shutil.copy2(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100667 self.assertTrue(os.path.islink(dst))
668 self.assertEqual(os.readlink(dst), os.readlink(src_link))
669 dst_stat = os.lstat(dst)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700670 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100671 for attr in 'st_atime', 'st_mtime':
672 # The modification times may be truncated in the new file.
673 self.assertLessEqual(getattr(src_link_stat, attr),
674 getattr(dst_stat, attr) + 1)
675 if hasattr(os, 'lchmod'):
676 self.assertEqual(src_link_stat.st_mode, dst_stat.st_mode)
677 self.assertNotEqual(src_stat.st_mode, dst_stat.st_mode)
678 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
679 self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags)
680
Antoine Pitrou424246f2012-05-12 19:02:01 +0200681 @support.skip_unless_xattr
682 def test_copy2_xattr(self):
683 tmp_dir = self.mkdtemp()
684 src = os.path.join(tmp_dir, 'foo')
685 dst = os.path.join(tmp_dir, 'bar')
686 write_file(src, 'foo')
687 os.setxattr(src, 'user.foo', b'42')
688 shutil.copy2(src, dst)
689 self.assertEqual(
690 os.getxattr(src, 'user.foo'),
691 os.getxattr(dst, 'user.foo'))
692 os.remove(dst)
693
Antoine Pitrou78091e62011-12-29 18:54:15 +0100694 @support.skip_unless_symlink
695 def test_copyfile_symlinks(self):
696 tmp_dir = self.mkdtemp()
697 src = os.path.join(tmp_dir, 'src')
698 dst = os.path.join(tmp_dir, 'dst')
699 dst_link = os.path.join(tmp_dir, 'dst_link')
700 link = os.path.join(tmp_dir, 'link')
701 write_file(src, 'foo')
702 os.symlink(src, link)
703 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700704 shutil.copyfile(link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100705 self.assertTrue(os.path.islink(dst_link))
706 self.assertEqual(os.readlink(link), os.readlink(dst_link))
707 # follow
708 shutil.copyfile(link, dst)
709 self.assertFalse(os.path.islink(dst))
710
Hynek Schlawack2100b422012-06-23 20:28:32 +0200711 def test_rmtree_uses_safe_fd_version_if_available(self):
Hynek Schlawackd0f6e0a2012-06-29 08:28:20 +0200712 _use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
713 os.supports_dir_fd and
714 os.listdir in os.supports_fd and
715 os.stat in os.supports_follow_symlinks)
716 if _use_fd_functions:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200717 self.assertTrue(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000718 self.assertTrue(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200719 tmp_dir = self.mkdtemp()
720 d = os.path.join(tmp_dir, 'a')
721 os.mkdir(d)
722 try:
723 real_rmtree = shutil._rmtree_safe_fd
724 class Called(Exception): pass
725 def _raiser(*args, **kwargs):
726 raise Called
727 shutil._rmtree_safe_fd = _raiser
728 self.assertRaises(Called, shutil.rmtree, d)
729 finally:
730 shutil._rmtree_safe_fd = real_rmtree
731 else:
732 self.assertFalse(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000733 self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200734
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000735 def test_rmtree_dont_delete_file(self):
736 # When called on a file instead of a directory, don't delete it.
737 handle, path = tempfile.mkstemp()
Victor Stinnerbf816222011-06-30 23:25:47 +0200738 os.close(handle)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200739 self.assertRaises(NotADirectoryError, shutil.rmtree, path)
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000740 os.remove(path)
741
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000742 def test_copytree_simple(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000743 src_dir = tempfile.mkdtemp()
744 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200745 self.addCleanup(shutil.rmtree, src_dir)
746 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
747 write_file((src_dir, 'test.txt'), '123')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000748 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200749 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000750
Éric Araujoa7e33a12011-08-12 19:51:35 +0200751 shutil.copytree(src_dir, dst_dir)
752 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test.txt')))
753 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'test_dir')))
754 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test_dir',
755 'test.txt')))
756 actual = read_file((dst_dir, 'test.txt'))
757 self.assertEqual(actual, '123')
758 actual = read_file((dst_dir, 'test_dir', 'test.txt'))
759 self.assertEqual(actual, '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000760
jab9e00d9e2018-12-28 13:03:40 -0500761 def test_copytree_dirs_exist_ok(self):
762 src_dir = tempfile.mkdtemp()
763 dst_dir = tempfile.mkdtemp()
764 self.addCleanup(shutil.rmtree, src_dir)
765 self.addCleanup(shutil.rmtree, dst_dir)
766
767 write_file((src_dir, 'nonexisting.txt'), '123')
768 os.mkdir(os.path.join(src_dir, 'existing_dir'))
769 os.mkdir(os.path.join(dst_dir, 'existing_dir'))
770 write_file((dst_dir, 'existing_dir', 'existing.txt'), 'will be replaced')
771 write_file((src_dir, 'existing_dir', 'existing.txt'), 'has been replaced')
772
773 shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)
774 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'nonexisting.txt')))
775 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'existing_dir')))
776 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'existing_dir',
777 'existing.txt')))
778 actual = read_file((dst_dir, 'nonexisting.txt'))
779 self.assertEqual(actual, '123')
780 actual = read_file((dst_dir, 'existing_dir', 'existing.txt'))
781 self.assertEqual(actual, 'has been replaced')
782
783 with self.assertRaises(FileExistsError):
784 shutil.copytree(src_dir, dst_dir, dirs_exist_ok=False)
785
Antoine Pitrou78091e62011-12-29 18:54:15 +0100786 @support.skip_unless_symlink
787 def test_copytree_symlinks(self):
788 tmp_dir = self.mkdtemp()
789 src_dir = os.path.join(tmp_dir, 'src')
790 dst_dir = os.path.join(tmp_dir, 'dst')
791 sub_dir = os.path.join(src_dir, 'sub')
792 os.mkdir(src_dir)
793 os.mkdir(sub_dir)
794 write_file((src_dir, 'file.txt'), 'foo')
795 src_link = os.path.join(sub_dir, 'link')
796 dst_link = os.path.join(dst_dir, 'sub/link')
797 os.symlink(os.path.join(src_dir, 'file.txt'),
798 src_link)
799 if hasattr(os, 'lchmod'):
800 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
801 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
802 os.lchflags(src_link, stat.UF_NODUMP)
803 src_stat = os.lstat(src_link)
804 shutil.copytree(src_dir, dst_dir, symlinks=True)
805 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'sub', 'link')))
Steve Dower9eb3d542019-08-21 15:52:42 -0700806 actual = os.readlink(os.path.join(dst_dir, 'sub', 'link'))
807 # Bad practice to blindly strip the prefix as it may be required to
808 # correctly refer to the file, but we're only comparing paths here.
809 if os.name == 'nt' and actual.startswith('\\\\?\\'):
810 actual = actual[4:]
811 self.assertEqual(actual, os.path.join(src_dir, 'file.txt'))
Antoine Pitrou78091e62011-12-29 18:54:15 +0100812 dst_stat = os.lstat(dst_link)
813 if hasattr(os, 'lchmod'):
814 self.assertEqual(dst_stat.st_mode, src_stat.st_mode)
815 if hasattr(os, 'lchflags'):
816 self.assertEqual(dst_stat.st_flags, src_stat.st_flags)
817
Georg Brandl2ee470f2008-07-16 12:55:28 +0000818 def test_copytree_with_exclude(self):
Georg Brandl2ee470f2008-07-16 12:55:28 +0000819 # creating data
820 join = os.path.join
821 exists = os.path.exists
822 src_dir = tempfile.mkdtemp()
Georg Brandl2ee470f2008-07-16 12:55:28 +0000823 try:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000824 dst_dir = join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200825 write_file((src_dir, 'test.txt'), '123')
826 write_file((src_dir, 'test.tmp'), '123')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000827 os.mkdir(join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200828 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000829 os.mkdir(join(src_dir, 'test_dir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200830 write_file((src_dir, 'test_dir2', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000831 os.mkdir(join(src_dir, 'test_dir2', 'subdir'))
832 os.mkdir(join(src_dir, 'test_dir2', 'subdir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200833 write_file((src_dir, 'test_dir2', 'subdir', 'test.txt'), '456')
834 write_file((src_dir, 'test_dir2', 'subdir2', 'test.py'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000835
836 # testing glob-like patterns
837 try:
838 patterns = shutil.ignore_patterns('*.tmp', 'test_dir2')
839 shutil.copytree(src_dir, dst_dir, ignore=patterns)
840 # checking the result: some elements should not be copied
841 self.assertTrue(exists(join(dst_dir, 'test.txt')))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200842 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
843 self.assertFalse(exists(join(dst_dir, 'test_dir2')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000844 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200845 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000846 try:
847 patterns = shutil.ignore_patterns('*.tmp', 'subdir*')
848 shutil.copytree(src_dir, dst_dir, ignore=patterns)
849 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200850 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
851 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2')))
852 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000853 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200854 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000855
856 # testing callable-style
857 try:
858 def _filter(src, names):
859 res = []
860 for name in names:
861 path = os.path.join(src, name)
862
863 if (os.path.isdir(path) and
864 path.split()[-1] == 'subdir'):
865 res.append(name)
866 elif os.path.splitext(path)[-1] in ('.py'):
867 res.append(name)
868 return res
869
870 shutil.copytree(src_dir, dst_dir, ignore=_filter)
871
872 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200873 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2',
874 'test.py')))
875 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000876
877 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200878 shutil.rmtree(dst_dir)
Georg Brandl2ee470f2008-07-16 12:55:28 +0000879 finally:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000880 shutil.rmtree(src_dir)
881 shutil.rmtree(os.path.dirname(dst_dir))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000882
Antoine Pitrouac601602013-08-16 19:35:02 +0200883 def test_copytree_retains_permissions(self):
884 tmp_dir = tempfile.mkdtemp()
885 src_dir = os.path.join(tmp_dir, 'source')
886 os.mkdir(src_dir)
887 dst_dir = os.path.join(tmp_dir, 'destination')
888 self.addCleanup(shutil.rmtree, tmp_dir)
889
890 os.chmod(src_dir, 0o777)
891 write_file((src_dir, 'permissive.txt'), '123')
892 os.chmod(os.path.join(src_dir, 'permissive.txt'), 0o777)
893 write_file((src_dir, 'restrictive.txt'), '456')
894 os.chmod(os.path.join(src_dir, 'restrictive.txt'), 0o600)
895 restrictive_subdir = tempfile.mkdtemp(dir=src_dir)
896 os.chmod(restrictive_subdir, 0o600)
897
898 shutil.copytree(src_dir, dst_dir)
Brett Cannon9c7eb552013-08-23 14:38:11 -0400899 self.assertEqual(os.stat(src_dir).st_mode, os.stat(dst_dir).st_mode)
900 self.assertEqual(os.stat(os.path.join(src_dir, 'permissive.txt')).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200901 os.stat(os.path.join(dst_dir, 'permissive.txt')).st_mode)
Brett Cannon9c7eb552013-08-23 14:38:11 -0400902 self.assertEqual(os.stat(os.path.join(src_dir, 'restrictive.txt')).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200903 os.stat(os.path.join(dst_dir, 'restrictive.txt')).st_mode)
904 restrictive_subdir_dst = os.path.join(dst_dir,
905 os.path.split(restrictive_subdir)[1])
Brett Cannon9c7eb552013-08-23 14:38:11 -0400906 self.assertEqual(os.stat(restrictive_subdir).st_mode,
Antoine Pitrouac601602013-08-16 19:35:02 +0200907 os.stat(restrictive_subdir_dst).st_mode)
908
Berker Peksag884afd92014-12-10 02:50:32 +0200909 @unittest.mock.patch('os.chmod')
910 def test_copytree_winerror(self, mock_patch):
911 # When copying to VFAT, copystat() raises OSError. On Windows, the
912 # exception object has a meaningful 'winerror' attribute, but not
913 # on other operating systems. Do not assume 'winerror' is set.
914 src_dir = tempfile.mkdtemp()
915 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
916 self.addCleanup(shutil.rmtree, src_dir)
917 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
918
919 mock_patch.side_effect = PermissionError('ka-boom')
920 with self.assertRaises(shutil.Error):
921 shutil.copytree(src_dir, dst_dir)
922
Giampaolo Rodolac606a9c2019-02-26 12:04:41 +0100923 def test_copytree_custom_copy_function(self):
924 # See: https://bugs.python.org/issue35648
925 def custom_cpfun(a, b):
926 flag.append(None)
927 self.assertIsInstance(a, str)
928 self.assertIsInstance(b, str)
929 self.assertEqual(a, os.path.join(src, 'foo'))
930 self.assertEqual(b, os.path.join(dst, 'foo'))
931
932 flag = []
933 src = tempfile.mkdtemp()
Miss Islington (bot)7fe81ce2019-06-26 16:57:49 -0700934 self.addCleanup(support.rmtree, src)
Giampaolo Rodolac606a9c2019-02-26 12:04:41 +0100935 dst = tempfile.mktemp()
Miss Islington (bot)7fe81ce2019-06-26 16:57:49 -0700936 self.addCleanup(support.rmtree, dst)
Giampaolo Rodolac606a9c2019-02-26 12:04:41 +0100937 with open(os.path.join(src, 'foo'), 'w') as f:
938 f.close()
939 shutil.copytree(src, dst, copy_function=custom_cpfun)
940 self.assertEqual(len(flag), 1)
941
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000942 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000943 def test_dont_copy_file_onto_link_to_itself(self):
944 # bug 851123.
945 os.mkdir(TESTFN)
946 src = os.path.join(TESTFN, 'cheese')
947 dst = os.path.join(TESTFN, 'shop')
948 try:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000949 with open(src, 'w') as f:
950 f.write('cheddar')
xdegaye92c2ca72017-11-12 17:31:07 +0100951 try:
952 os.link(src, dst)
953 except PermissionError as e:
954 self.skipTest('os.link(): %s' % e)
Hynek Schlawack48653762012-10-07 12:49:58 +0200955 self.assertRaises(shutil.SameFileError, shutil.copyfile, src, dst)
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000956 with open(src, 'r') as f:
957 self.assertEqual(f.read(), 'cheddar')
958 os.remove(dst)
959 finally:
960 shutil.rmtree(TESTFN, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000961
Brian Curtin3b4499c2010-12-28 14:31:47 +0000962 @support.skip_unless_symlink
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000963 def test_dont_copy_file_onto_symlink_to_itself(self):
964 # bug 851123.
965 os.mkdir(TESTFN)
966 src = os.path.join(TESTFN, 'cheese')
967 dst = os.path.join(TESTFN, 'shop')
968 try:
969 with open(src, 'w') as f:
970 f.write('cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000971 # Using `src` here would mean we end up with a symlink pointing
972 # to TESTFN/TESTFN/cheese, while it should point at
973 # TESTFN/cheese.
974 os.symlink('cheese', dst)
Hynek Schlawack48653762012-10-07 12:49:58 +0200975 self.assertRaises(shutil.SameFileError, shutil.copyfile, src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000976 with open(src, 'r') as f:
977 self.assertEqual(f.read(), 'cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000978 os.remove(dst)
979 finally:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000980 shutil.rmtree(TESTFN, ignore_errors=True)
Johannes Gijsbers68128712004-08-14 13:57:08 +0000981
Brian Curtin3b4499c2010-12-28 14:31:47 +0000982 @support.skip_unless_symlink
Brian Curtind40e6f72010-07-08 21:39:08 +0000983 def test_rmtree_on_symlink(self):
984 # bug 1669.
985 os.mkdir(TESTFN)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000986 try:
Brian Curtind40e6f72010-07-08 21:39:08 +0000987 src = os.path.join(TESTFN, 'cheese')
988 dst = os.path.join(TESTFN, 'shop')
989 os.mkdir(src)
990 os.symlink(src, dst)
991 self.assertRaises(OSError, shutil.rmtree, dst)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200992 shutil.rmtree(dst, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000993 finally:
Brian Curtind40e6f72010-07-08 21:39:08 +0000994 shutil.rmtree(TESTFN, ignore_errors=True)
995
Steve Dower9eb3d542019-08-21 15:52:42 -0700996 @unittest.skipUnless(_winapi, 'only relevant on Windows')
997 def test_rmtree_on_junction(self):
998 os.mkdir(TESTFN)
999 try:
1000 src = os.path.join(TESTFN, 'cheese')
1001 dst = os.path.join(TESTFN, 'shop')
1002 os.mkdir(src)
1003 open(os.path.join(src, 'spam'), 'wb').close()
1004 _winapi.CreateJunction(src, dst)
1005 self.assertRaises(OSError, shutil.rmtree, dst)
1006 shutil.rmtree(dst, ignore_errors=True)
1007 finally:
1008 shutil.rmtree(TESTFN, ignore_errors=True)
1009
Serhiy Storchaka43767632013-11-03 21:31:38 +02001010 # Issue #3002: copyfile and copytree block indefinitely on named pipes
1011 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
1012 def test_copyfile_named_pipe(self):
xdegaye92c2ca72017-11-12 17:31:07 +01001013 try:
1014 os.mkfifo(TESTFN)
1015 except PermissionError as e:
1016 self.skipTest('os.mkfifo(): %s' % e)
Serhiy Storchaka43767632013-11-03 21:31:38 +02001017 try:
1018 self.assertRaises(shutil.SpecialFileError,
1019 shutil.copyfile, TESTFN, TESTFN2)
1020 self.assertRaises(shutil.SpecialFileError,
1021 shutil.copyfile, __file__, TESTFN)
1022 finally:
1023 os.remove(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +00001024
Serhiy Storchaka43767632013-11-03 21:31:38 +02001025 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
1026 @support.skip_unless_symlink
1027 def test_copytree_named_pipe(self):
1028 os.mkdir(TESTFN)
1029 try:
1030 subdir = os.path.join(TESTFN, "subdir")
1031 os.mkdir(subdir)
1032 pipe = os.path.join(subdir, "mypipe")
xdegaye92c2ca72017-11-12 17:31:07 +01001033 try:
1034 os.mkfifo(pipe)
1035 except PermissionError as e:
1036 self.skipTest('os.mkfifo(): %s' % e)
Antoine Pitrou7fff0962009-05-01 21:09:44 +00001037 try:
Serhiy Storchaka43767632013-11-03 21:31:38 +02001038 shutil.copytree(TESTFN, TESTFN2)
1039 except shutil.Error as e:
1040 errors = e.args[0]
1041 self.assertEqual(len(errors), 1)
1042 src, dst, error_msg = errors[0]
1043 self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
1044 else:
1045 self.fail("shutil.Error should have been raised")
1046 finally:
1047 shutil.rmtree(TESTFN, ignore_errors=True)
1048 shutil.rmtree(TESTFN2, ignore_errors=True)
Antoine Pitrou7fff0962009-05-01 21:09:44 +00001049
Tarek Ziadé5340db32010-04-19 22:30:51 +00001050 def test_copytree_special_func(self):
1051
1052 src_dir = self.mkdtemp()
1053 dst_dir = os.path.join(self.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +02001054 write_file((src_dir, 'test.txt'), '123')
Tarek Ziadé5340db32010-04-19 22:30:51 +00001055 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +02001056 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadé5340db32010-04-19 22:30:51 +00001057
1058 copied = []
1059 def _copy(src, dst):
1060 copied.append((src, dst))
1061
1062 shutil.copytree(src_dir, dst_dir, copy_function=_copy)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001063 self.assertEqual(len(copied), 2)
Tarek Ziadé5340db32010-04-19 22:30:51 +00001064
Brian Curtin3b4499c2010-12-28 14:31:47 +00001065 @support.skip_unless_symlink
Tarek Ziadéfb437512010-04-20 08:57:33 +00001066 def test_copytree_dangling_symlinks(self):
1067
1068 # a dangling symlink raises an error at the end
1069 src_dir = self.mkdtemp()
1070 dst_dir = os.path.join(self.mkdtemp(), 'destination')
1071 os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt'))
1072 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +02001073 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadéfb437512010-04-20 08:57:33 +00001074 self.assertRaises(Error, shutil.copytree, src_dir, dst_dir)
1075
1076 # a dangling symlink is ignored with the proper flag
1077 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
1078 shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)
1079 self.assertNotIn('test.txt', os.listdir(dst_dir))
1080
1081 # a dangling symlink is copied if symlinks=True
1082 dst_dir = os.path.join(self.mkdtemp(), 'destination3')
1083 shutil.copytree(src_dir, dst_dir, symlinks=True)
1084 self.assertIn('test.txt', os.listdir(dst_dir))
1085
Berker Peksag5a294d82015-07-25 14:53:48 +03001086 @support.skip_unless_symlink
1087 def test_copytree_symlink_dir(self):
1088 src_dir = self.mkdtemp()
1089 dst_dir = os.path.join(self.mkdtemp(), 'destination')
1090 os.mkdir(os.path.join(src_dir, 'real_dir'))
1091 with open(os.path.join(src_dir, 'real_dir', 'test.txt'), 'w'):
1092 pass
1093 os.symlink(os.path.join(src_dir, 'real_dir'),
1094 os.path.join(src_dir, 'link_to_dir'),
1095 target_is_directory=True)
1096
1097 shutil.copytree(src_dir, dst_dir, symlinks=False)
1098 self.assertFalse(os.path.islink(os.path.join(dst_dir, 'link_to_dir')))
1099 self.assertIn('test.txt', os.listdir(os.path.join(dst_dir, 'link_to_dir')))
1100
1101 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
1102 shutil.copytree(src_dir, dst_dir, symlinks=True)
1103 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'link_to_dir')))
1104 self.assertIn('test.txt', os.listdir(os.path.join(dst_dir, 'link_to_dir')))
1105
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001106 def _copy_file(self, method):
1107 fname = 'test.txt'
1108 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +02001109 write_file((tmpdir, fname), 'xxx')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001110 file1 = os.path.join(tmpdir, fname)
1111 tmpdir2 = self.mkdtemp()
1112 method(file1, tmpdir2)
1113 file2 = os.path.join(tmpdir2, fname)
1114 return (file1, file2)
1115
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001116 def test_copy(self):
1117 # Ensure that the copied file exists and has the same mode bits.
1118 file1, file2 = self._copy_file(shutil.copy)
1119 self.assertTrue(os.path.exists(file2))
1120 self.assertEqual(os.stat(file1).st_mode, os.stat(file2).st_mode)
1121
Senthil Kumaran0c2dba52011-07-03 18:21:38 -07001122 @unittest.skipUnless(hasattr(os, 'utime'), 'requires os.utime')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001123 def test_copy2(self):
1124 # Ensure that the copied file exists and has the same mode and
1125 # modification time bits.
1126 file1, file2 = self._copy_file(shutil.copy2)
1127 self.assertTrue(os.path.exists(file2))
1128 file1_stat = os.stat(file1)
1129 file2_stat = os.stat(file2)
1130 self.assertEqual(file1_stat.st_mode, file2_stat.st_mode)
1131 for attr in 'st_atime', 'st_mtime':
1132 # The modification times may be truncated in the new file.
1133 self.assertLessEqual(getattr(file1_stat, attr),
1134 getattr(file2_stat, attr) + 1)
1135 if hasattr(os, 'chflags') and hasattr(file1_stat, 'st_flags'):
1136 self.assertEqual(getattr(file1_stat, 'st_flags'),
1137 getattr(file2_stat, 'st_flags'))
1138
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001139 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001140 def test_make_tarball(self):
1141 # creating something to tar
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001142 root_dir, base_dir = self._create_files('')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001143
1144 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001145 # force shutil to create the directory
1146 os.rmdir(tmpdir2)
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001147 # working with relative paths
1148 work_dir = os.path.dirname(tmpdir2)
1149 rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001150
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001151 with support.change_cwd(work_dir):
Serhiy Storchaka5558d4f2015-09-08 09:59:02 +03001152 base_name = os.path.abspath(rel_base_name)
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001153 tarball = make_archive(rel_base_name, 'gztar', root_dir, '.')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001154
1155 # check if the compressed tarball was created
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001156 self.assertEqual(tarball, base_name + '.tar.gz')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001157 self.assertTrue(os.path.isfile(tarball))
1158 self.assertTrue(tarfile.is_tarfile(tarball))
1159 with tarfile.open(tarball, 'r:gz') as tf:
1160 self.assertCountEqual(tf.getnames(),
1161 ['.', './sub', './sub2',
1162 './file1', './file2', './sub/file3'])
Tarek Ziadé396fad72010-02-23 05:30:31 +00001163
1164 # trying an uncompressed one
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001165 with support.change_cwd(work_dir):
1166 tarball = make_archive(rel_base_name, 'tar', root_dir, '.')
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001167 self.assertEqual(tarball, base_name + '.tar')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001168 self.assertTrue(os.path.isfile(tarball))
1169 self.assertTrue(tarfile.is_tarfile(tarball))
1170 with tarfile.open(tarball, 'r') as tf:
1171 self.assertCountEqual(tf.getnames(),
1172 ['.', './sub', './sub2',
1173 './file1', './file2', './sub/file3'])
Tarek Ziadé396fad72010-02-23 05:30:31 +00001174
1175 def _tarinfo(self, path):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001176 with tarfile.open(path) as tar:
Tarek Ziadé396fad72010-02-23 05:30:31 +00001177 names = tar.getnames()
1178 names.sort()
1179 return tuple(names)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001180
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001181 def _create_files(self, base_dir='dist'):
Tarek Ziadé396fad72010-02-23 05:30:31 +00001182 # creating something to tar
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001183 root_dir = self.mkdtemp()
1184 dist = os.path.join(root_dir, base_dir)
1185 os.makedirs(dist, exist_ok=True)
Éric Araujoa7e33a12011-08-12 19:51:35 +02001186 write_file((dist, 'file1'), 'xxx')
1187 write_file((dist, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001188 os.mkdir(os.path.join(dist, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +02001189 write_file((dist, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001190 os.mkdir(os.path.join(dist, 'sub2'))
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001191 if base_dir:
1192 write_file((root_dir, 'outer'), 'xxx')
1193 return root_dir, base_dir
Tarek Ziadé396fad72010-02-23 05:30:31 +00001194
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001195 @support.requires_zlib
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001196 @unittest.skipUnless(shutil.which('tar'),
Tarek Ziadé396fad72010-02-23 05:30:31 +00001197 'Need the tar command to run')
1198 def test_tarfile_vs_tar(self):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001199 root_dir, base_dir = self._create_files()
1200 base_name = os.path.join(self.mkdtemp(), 'archive')
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001201 tarball = make_archive(base_name, 'gztar', root_dir, base_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001202
1203 # check if the compressed tarball was created
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001204 self.assertEqual(tarball, base_name + '.tar.gz')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001205 self.assertTrue(os.path.isfile(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001206
1207 # now create another tarball using `tar`
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001208 tarball2 = os.path.join(root_dir, 'archive2.tar')
1209 tar_cmd = ['tar', '-cf', 'archive2.tar', base_dir]
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001210 subprocess.check_call(tar_cmd, cwd=root_dir,
1211 stdout=subprocess.DEVNULL)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001212
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001213 self.assertTrue(os.path.isfile(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001214 # let's compare both tarballs
Ezio Melottib3aedd42010-11-20 19:04:17 +00001215 self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001216
1217 # trying an uncompressed one
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001218 tarball = make_archive(base_name, 'tar', root_dir, base_dir)
1219 self.assertEqual(tarball, base_name + '.tar')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001220 self.assertTrue(os.path.isfile(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001221
1222 # now for a dry_run
Serhiy Storchakaa091a822015-09-07 13:55:25 +03001223 tarball = make_archive(base_name, 'tar', root_dir, base_dir,
1224 dry_run=True)
1225 self.assertEqual(tarball, base_name + '.tar')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001226 self.assertTrue(os.path.isfile(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001227
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001228 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001229 def test_make_zipfile(self):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001230 # creating something to zip
1231 root_dir, base_dir = self._create_files()
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001232
1233 tmpdir2 = self.mkdtemp()
1234 # force shutil to create the directory
1235 os.rmdir(tmpdir2)
1236 # working with relative paths
1237 work_dir = os.path.dirname(tmpdir2)
1238 rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive')
Serhiy Storchakaeba8fee2015-09-07 19:58:23 +03001239
1240 with support.change_cwd(work_dir):
Serhiy Storchaka5558d4f2015-09-08 09:59:02 +03001241 base_name = os.path.abspath(rel_base_name)
Serhiy Storchaka666de772016-10-23 15:55:09 +03001242 res = make_archive(rel_base_name, 'zip', root_dir)
1243
1244 self.assertEqual(res, base_name + '.zip')
1245 self.assertTrue(os.path.isfile(res))
1246 self.assertTrue(zipfile.is_zipfile(res))
1247 with zipfile.ZipFile(res) as zf:
1248 self.assertCountEqual(zf.namelist(),
1249 ['dist/', 'dist/sub/', 'dist/sub2/',
1250 'dist/file1', 'dist/file2', 'dist/sub/file3',
1251 'outer'])
1252
1253 with support.change_cwd(work_dir):
1254 base_name = os.path.abspath(rel_base_name)
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001255 res = make_archive(rel_base_name, 'zip', root_dir, base_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001256
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001257 self.assertEqual(res, base_name + '.zip')
1258 self.assertTrue(os.path.isfile(res))
1259 self.assertTrue(zipfile.is_zipfile(res))
1260 with zipfile.ZipFile(res) as zf:
1261 self.assertCountEqual(zf.namelist(),
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001262 ['dist/', 'dist/sub/', 'dist/sub2/',
1263 'dist/file1', 'dist/file2', 'dist/sub/file3'])
Tarek Ziadé396fad72010-02-23 05:30:31 +00001264
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001265 @support.requires_zlib
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001266 @unittest.skipUnless(shutil.which('zip'),
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001267 'Need the zip command to run')
1268 def test_zipfile_vs_zip(self):
1269 root_dir, base_dir = self._create_files()
1270 base_name = os.path.join(self.mkdtemp(), 'archive')
1271 archive = make_archive(base_name, 'zip', root_dir, base_dir)
1272
1273 # check if ZIP file was created
1274 self.assertEqual(archive, base_name + '.zip')
1275 self.assertTrue(os.path.isfile(archive))
1276
1277 # now create another ZIP file using `zip`
1278 archive2 = os.path.join(root_dir, 'archive2.zip')
1279 zip_cmd = ['zip', '-q', '-r', 'archive2.zip', base_dir]
Serhiy Storchakab42de2f2015-11-21 14:09:26 +02001280 subprocess.check_call(zip_cmd, cwd=root_dir,
1281 stdout=subprocess.DEVNULL)
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001282
1283 self.assertTrue(os.path.isfile(archive2))
1284 # let's compare both ZIP files
1285 with zipfile.ZipFile(archive) as zf:
1286 names = zf.namelist()
1287 with zipfile.ZipFile(archive2) as zf:
1288 names2 = zf.namelist()
1289 self.assertEqual(sorted(names), sorted(names2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001290
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001291 @support.requires_zlib
Serhiy Storchaka8bc792a2015-11-22 14:49:58 +02001292 @unittest.skipUnless(shutil.which('unzip'),
1293 'Need the unzip command to run')
1294 def test_unzip_zipfile(self):
1295 root_dir, base_dir = self._create_files()
1296 base_name = os.path.join(self.mkdtemp(), 'archive')
1297 archive = make_archive(base_name, 'zip', root_dir, base_dir)
1298
1299 # check if ZIP file was created
1300 self.assertEqual(archive, base_name + '.zip')
1301 self.assertTrue(os.path.isfile(archive))
1302
1303 # now check the ZIP file using `unzip -t`
1304 zip_cmd = ['unzip', '-t', archive]
1305 with support.change_cwd(root_dir):
1306 try:
1307 subprocess.check_output(zip_cmd, stderr=subprocess.STDOUT)
1308 except subprocess.CalledProcessError as exc:
1309 details = exc.output.decode(errors="replace")
Benjamin Petersona710ebd2018-09-13 10:08:46 -07001310 if 'unrecognized option: t' in details:
Benjamin Petersone78734d2018-09-13 10:57:23 -07001311 self.skipTest("unzip doesn't support -t")
Serhiy Storchaka8bc792a2015-11-22 14:49:58 +02001312 msg = "{}\n\n**Unzip Output**\n{}"
1313 self.fail(msg.format(exc, details))
1314
Tarek Ziadé396fad72010-02-23 05:30:31 +00001315 def test_make_archive(self):
1316 tmpdir = self.mkdtemp()
1317 base_name = os.path.join(tmpdir, 'archive')
1318 self.assertRaises(ValueError, make_archive, base_name, 'xxx')
1319
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001320 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001321 def test_make_archive_owner_group(self):
1322 # testing make_archive with owner and group, with various combinations
1323 # this works even if there's not gid/uid support
1324 if UID_GID_SUPPORT:
1325 group = grp.getgrgid(0)[0]
1326 owner = pwd.getpwuid(0)[0]
1327 else:
1328 group = owner = 'root'
1329
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001330 root_dir, base_dir = self._create_files()
1331 base_name = os.path.join(self.mkdtemp(), 'archive')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001332 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
1333 group=group)
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001334 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001335
1336 res = make_archive(base_name, 'zip', root_dir, base_dir)
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001337 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001338
1339 res = make_archive(base_name, 'tar', root_dir, base_dir,
1340 owner=owner, group=group)
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001341 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001342
1343 res = make_archive(base_name, 'tar', root_dir, base_dir,
1344 owner='kjhkjhkjg', group='oihohoh')
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001345 self.assertTrue(os.path.isfile(res))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001346
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001347
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001348 @support.requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001349 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1350 def test_tarfile_root_owner(self):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001351 root_dir, base_dir = self._create_files()
1352 base_name = os.path.join(self.mkdtemp(), 'archive')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001353 group = grp.getgrgid(0)[0]
1354 owner = pwd.getpwuid(0)[0]
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001355 with support.change_cwd(root_dir):
1356 archive_name = make_archive(base_name, 'gztar', root_dir, 'dist',
1357 owner=owner, group=group)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001358
1359 # check if the compressed tarball was created
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001360 self.assertTrue(os.path.isfile(archive_name))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001361
1362 # now checks the rights
1363 archive = tarfile.open(archive_name)
1364 try:
1365 for member in archive.getmembers():
Ezio Melottib3aedd42010-11-20 19:04:17 +00001366 self.assertEqual(member.uid, 0)
1367 self.assertEqual(member.gid, 0)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001368 finally:
1369 archive.close()
1370
1371 def test_make_archive_cwd(self):
1372 current_dir = os.getcwd()
1373 def _breaks(*args, **kw):
1374 raise RuntimeError()
1375
1376 register_archive_format('xxx', _breaks, [], 'xxx file')
1377 try:
1378 try:
1379 make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
1380 except Exception:
1381 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +00001382 self.assertEqual(os.getcwd(), current_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001383 finally:
1384 unregister_archive_format('xxx')
1385
Serhiy Storchaka9a4fc192014-11-28 00:48:46 +02001386 def test_make_tarfile_in_curdir(self):
1387 # Issue #21280
1388 root_dir = self.mkdtemp()
1389 with support.change_cwd(root_dir):
1390 self.assertEqual(make_archive('test', 'tar'), 'test.tar')
1391 self.assertTrue(os.path.isfile('test.tar'))
1392
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001393 @support.requires_zlib
Serhiy Storchaka9a4fc192014-11-28 00:48:46 +02001394 def test_make_zipfile_in_curdir(self):
1395 # Issue #21280
1396 root_dir = self.mkdtemp()
1397 with support.change_cwd(root_dir):
1398 self.assertEqual(make_archive('test', 'zip'), 'test.zip')
1399 self.assertTrue(os.path.isfile('test.zip'))
1400
Tarek Ziadé396fad72010-02-23 05:30:31 +00001401 def test_register_archive_format(self):
1402
1403 self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
1404 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1405 1)
1406 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1407 [(1, 2), (1, 2, 3)])
1408
1409 register_archive_format('xxx', lambda: x, [(1, 2)], 'xxx file')
1410 formats = [name for name, params in get_archive_formats()]
1411 self.assertIn('xxx', formats)
1412
1413 unregister_archive_format('xxx')
1414 formats = [name for name, params in get_archive_formats()]
1415 self.assertNotIn('xxx', formats)
1416
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001417 def check_unpack_archive(self, format):
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001418 self.check_unpack_archive_with_converter(format, lambda path: path)
1419 self.check_unpack_archive_with_converter(format, pathlib.Path)
Serhiy Storchakab21d1552018-03-02 11:53:51 +02001420 self.check_unpack_archive_with_converter(format, FakePath)
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001421
1422 def check_unpack_archive_with_converter(self, format, converter):
Serhiy Storchaka527ef072015-09-06 18:33:19 +03001423 root_dir, base_dir = self._create_files()
Serhiy Storchaka2504cec2015-09-08 05:47:23 +03001424 expected = rlistdir(root_dir)
1425 expected.remove('outer')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001426
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001427 base_name = os.path.join(self.mkdtemp(), 'archive')
1428 filename = make_archive(base_name, format, root_dir, base_dir)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001429
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001430 # let's try to unpack it now
1431 tmpdir2 = self.mkdtemp()
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001432 unpack_archive(converter(filename), converter(tmpdir2))
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001433 self.assertEqual(rlistdir(tmpdir2), expected)
1434
1435 # and again, this time with the format specified
1436 tmpdir3 = self.mkdtemp()
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001437 unpack_archive(converter(filename), converter(tmpdir3), format=format)
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001438 self.assertEqual(rlistdir(tmpdir3), expected)
1439
Jelle Zijlstraa12df7b2017-05-05 14:27:12 -07001440 self.assertRaises(shutil.ReadError, unpack_archive, converter(TESTFN))
1441 self.assertRaises(ValueError, unpack_archive, converter(TESTFN), format='xxx')
Nick Coghlanabf202d2011-03-16 13:52:20 -04001442
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001443 def test_unpack_archive_tar(self):
1444 self.check_unpack_archive('tar')
1445
1446 @support.requires_zlib
1447 def test_unpack_archive_gztar(self):
1448 self.check_unpack_archive('gztar')
1449
1450 @support.requires_bz2
1451 def test_unpack_archive_bztar(self):
1452 self.check_unpack_archive('bztar')
1453
1454 @support.requires_lzma
Michael Feltef110b12019-02-18 12:02:44 +01001455 @unittest.skipIf(AIX and not _maxdataOK(), "AIX MAXDATA must be 0x20000000 or larger")
Serhiy Storchaka20cdffd2016-12-16 18:58:33 +02001456 def test_unpack_archive_xztar(self):
1457 self.check_unpack_archive('xztar')
1458
1459 @support.requires_zlib
1460 def test_unpack_archive_zip(self):
1461 self.check_unpack_archive('zip')
1462
Martin Pantereb995702016-07-28 01:11:04 +00001463 def test_unpack_registry(self):
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001464
1465 formats = get_unpack_formats()
1466
1467 def _boo(filename, extract_dir, extra):
Ezio Melottib3aedd42010-11-20 19:04:17 +00001468 self.assertEqual(extra, 1)
1469 self.assertEqual(filename, 'stuff.boo')
1470 self.assertEqual(extract_dir, 'xx')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001471
1472 register_unpack_format('Boo', ['.boo', '.b2'], _boo, [('extra', 1)])
1473 unpack_archive('stuff.boo', 'xx')
1474
1475 # trying to register a .boo unpacker again
1476 self.assertRaises(RegistryError, register_unpack_format, 'Boo2',
1477 ['.boo'], _boo)
1478
1479 # should work now
1480 unregister_unpack_format('Boo')
1481 register_unpack_format('Boo2', ['.boo'], _boo)
1482 self.assertIn(('Boo2', ['.boo'], ''), get_unpack_formats())
1483 self.assertNotIn(('Boo', ['.boo'], ''), get_unpack_formats())
1484
1485 # let's leave a clean state
1486 unregister_unpack_format('Boo2')
Ezio Melottib3aedd42010-11-20 19:04:17 +00001487 self.assertEqual(get_unpack_formats(), formats)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001488
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001489 @unittest.skipUnless(hasattr(shutil, 'disk_usage'),
1490 "disk_usage not available on this platform")
1491 def test_disk_usage(self):
Gregory P. Smith529746c2017-07-06 17:11:27 -07001492 usage = shutil.disk_usage(os.path.dirname(__file__))
Victor Stinnerdc525f42018-12-11 12:05:21 +01001493 for attr in ('total', 'used', 'free'):
1494 self.assertIsInstance(getattr(usage, attr), int)
Éric Araujo2ee61882011-07-02 16:45:45 +02001495 self.assertGreater(usage.total, 0)
1496 self.assertGreater(usage.used, 0)
1497 self.assertGreaterEqual(usage.free, 0)
1498 self.assertGreaterEqual(usage.total, usage.used)
1499 self.assertGreater(usage.total, usage.free)
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001500
Victor Stinnerdc525f42018-12-11 12:05:21 +01001501 # bpo-32557: Check that disk_usage() also accepts a filename
1502 shutil.disk_usage(__file__)
1503
Sandro Tosid902a142011-08-22 23:28:27 +02001504 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1505 @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
1506 def test_chown(self):
1507
1508 # cleaned-up automatically by TestShutil.tearDown method
1509 dirname = self.mkdtemp()
1510 filename = tempfile.mktemp(dir=dirname)
1511 write_file(filename, 'testing chown function')
1512
1513 with self.assertRaises(ValueError):
1514 shutil.chown(filename)
1515
1516 with self.assertRaises(LookupError):
Raymond Hettinger15f44ab2016-08-30 10:47:49 -07001517 shutil.chown(filename, user='non-existing username')
Sandro Tosid902a142011-08-22 23:28:27 +02001518
1519 with self.assertRaises(LookupError):
Raymond Hettinger15f44ab2016-08-30 10:47:49 -07001520 shutil.chown(filename, group='non-existing groupname')
Sandro Tosid902a142011-08-22 23:28:27 +02001521
1522 with self.assertRaises(TypeError):
1523 shutil.chown(filename, b'spam')
1524
1525 with self.assertRaises(TypeError):
1526 shutil.chown(filename, 3.14)
1527
1528 uid = os.getuid()
1529 gid = os.getgid()
1530
1531 def check_chown(path, uid=None, gid=None):
1532 s = os.stat(filename)
1533 if uid is not None:
1534 self.assertEqual(uid, s.st_uid)
1535 if gid is not None:
1536 self.assertEqual(gid, s.st_gid)
1537
1538 shutil.chown(filename, uid, gid)
1539 check_chown(filename, uid, gid)
1540 shutil.chown(filename, uid)
1541 check_chown(filename, uid)
1542 shutil.chown(filename, user=uid)
1543 check_chown(filename, uid)
1544 shutil.chown(filename, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001545 check_chown(filename, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001546
1547 shutil.chown(dirname, uid, gid)
1548 check_chown(dirname, uid, gid)
1549 shutil.chown(dirname, uid)
1550 check_chown(dirname, uid)
1551 shutil.chown(dirname, user=uid)
1552 check_chown(dirname, uid)
1553 shutil.chown(dirname, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001554 check_chown(dirname, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001555
1556 user = pwd.getpwuid(uid)[0]
1557 group = grp.getgrgid(gid)[0]
1558 shutil.chown(filename, user, group)
1559 check_chown(filename, uid, gid)
1560 shutil.chown(dirname, user, group)
1561 check_chown(dirname, uid, gid)
1562
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001563 def test_copy_return_value(self):
1564 # copy and copy2 both return their destination path.
1565 for fn in (shutil.copy, shutil.copy2):
1566 src_dir = self.mkdtemp()
1567 dst_dir = self.mkdtemp()
1568 src = os.path.join(src_dir, 'foo')
1569 write_file(src, 'foo')
1570 rv = fn(src, dst_dir)
1571 self.assertEqual(rv, os.path.join(dst_dir, 'foo'))
1572 rv = fn(src, os.path.join(dst_dir, 'bar'))
1573 self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
1574
1575 def test_copyfile_return_value(self):
1576 # copytree returns its destination path.
1577 src_dir = self.mkdtemp()
1578 dst_dir = self.mkdtemp()
1579 dst_file = os.path.join(dst_dir, 'bar')
1580 src_file = os.path.join(src_dir, 'foo')
1581 write_file(src_file, 'foo')
1582 rv = shutil.copyfile(src_file, dst_file)
1583 self.assertTrue(os.path.exists(rv))
1584 self.assertEqual(read_file(src_file), read_file(dst_file))
1585
Hynek Schlawack48653762012-10-07 12:49:58 +02001586 def test_copyfile_same_file(self):
1587 # copyfile() should raise SameFileError if the source and destination
1588 # are the same.
1589 src_dir = self.mkdtemp()
1590 src_file = os.path.join(src_dir, 'foo')
1591 write_file(src_file, 'foo')
1592 self.assertRaises(SameFileError, shutil.copyfile, src_file, src_file)
Hynek Schlawack27ddb572012-10-28 13:59:27 +01001593 # But Error should work too, to stay backward compatible.
1594 self.assertRaises(Error, shutil.copyfile, src_file, src_file)
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02001595 # Make sure file is not corrupted.
1596 self.assertEqual(read_file(src_file), 'foo')
Hynek Schlawack48653762012-10-07 12:49:58 +02001597
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001598 def test_copytree_return_value(self):
1599 # copytree returns its destination path.
1600 src_dir = self.mkdtemp()
1601 dst_dir = src_dir + "dest"
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001602 self.addCleanup(shutil.rmtree, dst_dir, True)
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001603 src = os.path.join(src_dir, 'foo')
1604 write_file(src, 'foo')
1605 rv = shutil.copytree(src_dir, dst_dir)
1606 self.assertEqual(['foo'], os.listdir(rv))
1607
Christian Heimes9bd667a2008-01-20 15:14:11 +00001608
Brian Curtinc57a3452012-06-22 16:00:30 -05001609class TestWhich(unittest.TestCase):
1610
1611 def setUp(self):
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001612 self.temp_dir = tempfile.mkdtemp(prefix="Tmp")
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001613 self.addCleanup(shutil.rmtree, self.temp_dir, True)
Brian Curtinc57a3452012-06-22 16:00:30 -05001614 # Give the temp_file an ".exe" suffix for all.
1615 # It's needed on Windows and not harmful on other platforms.
1616 self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001617 prefix="Tmp",
1618 suffix=".Exe")
Brian Curtinc57a3452012-06-22 16:00:30 -05001619 os.chmod(self.temp_file.name, stat.S_IXUSR)
1620 self.addCleanup(self.temp_file.close)
1621 self.dir, self.file = os.path.split(self.temp_file.name)
Cheryl Sabella5680f652019-02-13 06:25:10 -05001622 self.env_path = self.dir
1623 self.curdir = os.curdir
1624 self.ext = ".EXE"
Brian Curtinc57a3452012-06-22 16:00:30 -05001625
1626 def test_basic(self):
1627 # Given an EXE in a directory, it should be returned.
1628 rv = shutil.which(self.file, path=self.dir)
1629 self.assertEqual(rv, self.temp_file.name)
1630
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001631 def test_absolute_cmd(self):
Brian Curtinc57a3452012-06-22 16:00:30 -05001632 # When given the fully qualified path to an executable that exists,
1633 # it should be returned.
1634 rv = shutil.which(self.temp_file.name, path=self.temp_dir)
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001635 self.assertEqual(rv, self.temp_file.name)
1636
1637 def test_relative_cmd(self):
1638 # When given the relative path with a directory part to an executable
1639 # that exists, it should be returned.
1640 base_dir, tail_dir = os.path.split(self.dir)
1641 relpath = os.path.join(tail_dir, self.file)
Nick Coghlan55175962013-07-28 22:11:50 +10001642 with support.change_cwd(path=base_dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001643 rv = shutil.which(relpath, path=self.temp_dir)
1644 self.assertEqual(rv, relpath)
1645 # But it shouldn't be searched in PATH directories (issue #16957).
Nick Coghlan55175962013-07-28 22:11:50 +10001646 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001647 rv = shutil.which(relpath, path=base_dir)
1648 self.assertIsNone(rv)
1649
1650 def test_cwd(self):
1651 # Issue #16957
1652 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001653 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001654 rv = shutil.which(self.file, path=base_dir)
1655 if sys.platform == "win32":
1656 # Windows: current directory implicitly on PATH
Cheryl Sabella5680f652019-02-13 06:25:10 -05001657 self.assertEqual(rv, os.path.join(self.curdir, self.file))
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001658 else:
1659 # Other platforms: shouldn't match in the current directory.
1660 self.assertIsNone(rv)
Brian Curtinc57a3452012-06-22 16:00:30 -05001661
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001662 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
1663 'non-root user required')
Brian Curtinc57a3452012-06-22 16:00:30 -05001664 def test_non_matching_mode(self):
1665 # Set the file read-only and ask for writeable files.
1666 os.chmod(self.temp_file.name, stat.S_IREAD)
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001667 if os.access(self.temp_file.name, os.W_OK):
1668 self.skipTest("can't set the file read-only")
Brian Curtinc57a3452012-06-22 16:00:30 -05001669 rv = shutil.which(self.file, path=self.dir, mode=os.W_OK)
1670 self.assertIsNone(rv)
1671
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001672 def test_relative_path(self):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001673 base_dir, tail_dir = os.path.split(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001674 with support.change_cwd(path=base_dir):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001675 rv = shutil.which(self.file, path=tail_dir)
1676 self.assertEqual(rv, os.path.join(tail_dir, self.file))
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001677
Brian Curtinc57a3452012-06-22 16:00:30 -05001678 def test_nonexistent_file(self):
1679 # Return None when no matching executable file is found on the path.
1680 rv = shutil.which("foo.exe", path=self.dir)
1681 self.assertIsNone(rv)
1682
1683 @unittest.skipUnless(sys.platform == "win32",
1684 "pathext check is Windows-only")
1685 def test_pathext_checking(self):
1686 # Ask for the file without the ".exe" extension, then ensure that
1687 # it gets found properly with the extension.
Serhiy Storchakad70127a2013-01-24 20:03:49 +02001688 rv = shutil.which(self.file[:-4], path=self.dir)
Cheryl Sabella5680f652019-02-13 06:25:10 -05001689 self.assertEqual(rv, self.temp_file.name[:-4] + self.ext)
Brian Curtinc57a3452012-06-22 16:00:30 -05001690
Barry Warsaw618738b2013-04-16 11:05:03 -04001691 def test_environ_path(self):
1692 with support.EnvironmentVarGuard() as env:
Cheryl Sabella5680f652019-02-13 06:25:10 -05001693 env['PATH'] = self.env_path
Barry Warsaw618738b2013-04-16 11:05:03 -04001694 rv = shutil.which(self.file)
1695 self.assertEqual(rv, self.temp_file.name)
1696
Victor Stinner228a3c92019-04-17 16:26:36 +02001697 def test_environ_path_empty(self):
1698 # PATH='': no match
1699 with support.EnvironmentVarGuard() as env:
1700 env['PATH'] = ''
1701 with unittest.mock.patch('os.confstr', return_value=self.dir, \
1702 create=True), \
1703 support.swap_attr(os, 'defpath', self.dir), \
1704 support.change_cwd(self.dir):
1705 rv = shutil.which(self.file)
1706 self.assertIsNone(rv)
1707
1708 def test_environ_path_cwd(self):
1709 expected_cwd = os.path.basename(self.temp_file.name)
1710 if sys.platform == "win32":
1711 curdir = os.curdir
1712 if isinstance(expected_cwd, bytes):
1713 curdir = os.fsencode(curdir)
1714 expected_cwd = os.path.join(curdir, expected_cwd)
1715
1716 # PATH=':': explicitly looks in the current directory
1717 with support.EnvironmentVarGuard() as env:
1718 env['PATH'] = os.pathsep
1719 with unittest.mock.patch('os.confstr', return_value=self.dir, \
1720 create=True), \
1721 support.swap_attr(os, 'defpath', self.dir):
1722 rv = shutil.which(self.file)
1723 self.assertIsNone(rv)
1724
1725 # look in current directory
1726 with support.change_cwd(self.dir):
1727 rv = shutil.which(self.file)
1728 self.assertEqual(rv, expected_cwd)
1729
1730 def test_environ_path_missing(self):
1731 with support.EnvironmentVarGuard() as env:
1732 env.pop('PATH', None)
1733
1734 # without confstr
1735 with unittest.mock.patch('os.confstr', side_effect=ValueError, \
1736 create=True), \
1737 support.swap_attr(os, 'defpath', self.dir):
1738 rv = shutil.which(self.file)
1739 self.assertEqual(rv, self.temp_file.name)
1740
1741 # with confstr
1742 with unittest.mock.patch('os.confstr', return_value=self.dir, \
1743 create=True), \
1744 support.swap_attr(os, 'defpath', ''):
1745 rv = shutil.which(self.file)
1746 self.assertEqual(rv, self.temp_file.name)
1747
Barry Warsaw618738b2013-04-16 11:05:03 -04001748 def test_empty_path(self):
1749 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001750 with support.change_cwd(path=self.dir), \
Barry Warsaw618738b2013-04-16 11:05:03 -04001751 support.EnvironmentVarGuard() as env:
Cheryl Sabella5680f652019-02-13 06:25:10 -05001752 env['PATH'] = self.env_path
Barry Warsaw618738b2013-04-16 11:05:03 -04001753 rv = shutil.which(self.file, path='')
1754 self.assertIsNone(rv)
1755
1756 def test_empty_path_no_PATH(self):
1757 with support.EnvironmentVarGuard() as env:
1758 env.pop('PATH', None)
1759 rv = shutil.which(self.file)
1760 self.assertIsNone(rv)
1761
Victor Stinner228a3c92019-04-17 16:26:36 +02001762 @unittest.skipUnless(sys.platform == "win32", 'test specific to Windows')
1763 def test_pathext(self):
1764 ext = ".xyz"
1765 temp_filexyz = tempfile.NamedTemporaryFile(dir=self.temp_dir,
1766 prefix="Tmp2", suffix=ext)
1767 os.chmod(temp_filexyz.name, stat.S_IXUSR)
1768 self.addCleanup(temp_filexyz.close)
1769
1770 # strip path and extension
1771 program = os.path.basename(temp_filexyz.name)
1772 program = os.path.splitext(program)[0]
1773
1774 with support.EnvironmentVarGuard() as env:
1775 env['PATHEXT'] = ext
1776 rv = shutil.which(program, path=self.temp_dir)
1777 self.assertEqual(rv, temp_filexyz.name)
1778
Brian Curtinc57a3452012-06-22 16:00:30 -05001779
Cheryl Sabella5680f652019-02-13 06:25:10 -05001780class TestWhichBytes(TestWhich):
1781 def setUp(self):
1782 TestWhich.setUp(self)
1783 self.dir = os.fsencode(self.dir)
1784 self.file = os.fsencode(self.file)
1785 self.temp_file.name = os.fsencode(self.temp_file.name)
1786 self.curdir = os.fsencode(self.curdir)
1787 self.ext = os.fsencode(self.ext)
1788
1789
Christian Heimesada8c3b2008-03-18 18:26:33 +00001790class TestMove(unittest.TestCase):
1791
1792 def setUp(self):
1793 filename = "foo"
Steve Dower66c0f012019-11-15 15:25:03 -08001794 basedir = None
1795 if sys.platform == "win32":
1796 basedir = os.path.realpath(os.getcwd())
1797 self.src_dir = tempfile.mkdtemp(dir=basedir)
1798 self.dst_dir = tempfile.mkdtemp(dir=basedir)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001799 self.src_file = os.path.join(self.src_dir, filename)
1800 self.dst_file = os.path.join(self.dst_dir, filename)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001801 with open(self.src_file, "wb") as f:
1802 f.write(b"spam")
1803
1804 def tearDown(self):
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001805 for d in (self.src_dir, self.dst_dir):
Christian Heimesada8c3b2008-03-18 18:26:33 +00001806 try:
1807 if d:
1808 shutil.rmtree(d)
1809 except:
1810 pass
1811
1812 def _check_move_file(self, src, dst, real_dst):
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001813 with open(src, "rb") as f:
1814 contents = f.read()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001815 shutil.move(src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001816 with open(real_dst, "rb") as f:
1817 self.assertEqual(contents, f.read())
Christian Heimesada8c3b2008-03-18 18:26:33 +00001818 self.assertFalse(os.path.exists(src))
1819
1820 def _check_move_dir(self, src, dst, real_dst):
1821 contents = sorted(os.listdir(src))
1822 shutil.move(src, dst)
1823 self.assertEqual(contents, sorted(os.listdir(real_dst)))
1824 self.assertFalse(os.path.exists(src))
1825
1826 def test_move_file(self):
1827 # Move a file to another location on the same filesystem.
1828 self._check_move_file(self.src_file, self.dst_file, self.dst_file)
1829
1830 def test_move_file_to_dir(self):
1831 # Move a file inside an existing dir on the same filesystem.
1832 self._check_move_file(self.src_file, self.dst_dir, self.dst_file)
1833
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001834 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001835 def test_move_file_other_fs(self):
1836 # Move a file to an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001837 self.test_move_file()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001838
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001839 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001840 def test_move_file_to_dir_other_fs(self):
1841 # Move a file to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001842 self.test_move_file_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001843
1844 def test_move_dir(self):
1845 # Move a dir to another location on the same filesystem.
1846 dst_dir = tempfile.mktemp()
1847 try:
1848 self._check_move_dir(self.src_dir, dst_dir, dst_dir)
1849 finally:
1850 try:
1851 shutil.rmtree(dst_dir)
1852 except:
1853 pass
1854
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001855 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001856 def test_move_dir_other_fs(self):
1857 # Move a dir to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001858 self.test_move_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001859
1860 def test_move_dir_to_dir(self):
1861 # Move a dir inside an existing dir on the same filesystem.
1862 self._check_move_dir(self.src_dir, self.dst_dir,
1863 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1864
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001865 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001866 def test_move_dir_to_dir_other_fs(self):
1867 # Move a dir inside an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001868 self.test_move_dir_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001869
Serhiy Storchaka3a308b92014-02-11 10:30:59 +02001870 def test_move_dir_sep_to_dir(self):
1871 self._check_move_dir(self.src_dir + os.path.sep, self.dst_dir,
1872 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1873
1874 @unittest.skipUnless(os.path.altsep, 'requires os.path.altsep')
1875 def test_move_dir_altsep_to_dir(self):
1876 self._check_move_dir(self.src_dir + os.path.altsep, self.dst_dir,
1877 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1878
Christian Heimesada8c3b2008-03-18 18:26:33 +00001879 def test_existing_file_inside_dest_dir(self):
1880 # A file with the same name inside the destination dir already exists.
1881 with open(self.dst_file, "wb"):
1882 pass
1883 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir)
1884
1885 def test_dont_move_dir_in_itself(self):
1886 # Moving a dir inside itself raises an Error.
1887 dst = os.path.join(self.src_dir, "bar")
1888 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst)
1889
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001890 def test_destinsrc_false_negative(self):
1891 os.mkdir(TESTFN)
1892 try:
1893 for src, dst in [('srcdir', 'srcdir/dest')]:
1894 src = os.path.join(TESTFN, src)
1895 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001896 self.assertTrue(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001897 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001898 'dst (%s) is not in src (%s)' % (dst, src))
1899 finally:
1900 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001901
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001902 def test_destinsrc_false_positive(self):
1903 os.mkdir(TESTFN)
1904 try:
1905 for src, dst in [('srcdir', 'src/dest'), ('srcdir', 'srcdir.new')]:
1906 src = os.path.join(TESTFN, src)
1907 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001908 self.assertFalse(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001909 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001910 'dst (%s) is in src (%s)' % (dst, src))
1911 finally:
1912 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimes9bd667a2008-01-20 15:14:11 +00001913
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001914 @support.skip_unless_symlink
1915 @mock_rename
1916 def test_move_file_symlink(self):
1917 dst = os.path.join(self.src_dir, 'bar')
1918 os.symlink(self.src_file, dst)
1919 shutil.move(dst, self.dst_file)
1920 self.assertTrue(os.path.islink(self.dst_file))
1921 self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
1922
1923 @support.skip_unless_symlink
1924 @mock_rename
1925 def test_move_file_symlink_to_dir(self):
1926 filename = "bar"
1927 dst = os.path.join(self.src_dir, filename)
1928 os.symlink(self.src_file, dst)
1929 shutil.move(dst, self.dst_dir)
1930 final_link = os.path.join(self.dst_dir, filename)
1931 self.assertTrue(os.path.islink(final_link))
1932 self.assertTrue(os.path.samefile(self.src_file, final_link))
1933
1934 @support.skip_unless_symlink
1935 @mock_rename
1936 def test_move_dangling_symlink(self):
1937 src = os.path.join(self.src_dir, 'baz')
1938 dst = os.path.join(self.src_dir, 'bar')
1939 os.symlink(src, dst)
1940 dst_link = os.path.join(self.dst_dir, 'quux')
1941 shutil.move(dst, dst_link)
1942 self.assertTrue(os.path.islink(dst_link))
Miss Islington (bot)c30c8692019-08-21 14:09:33 -07001943 self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001944
1945 @support.skip_unless_symlink
1946 @mock_rename
1947 def test_move_dir_symlink(self):
1948 src = os.path.join(self.src_dir, 'baz')
1949 dst = os.path.join(self.src_dir, 'bar')
1950 os.mkdir(src)
1951 os.symlink(src, dst)
1952 dst_link = os.path.join(self.dst_dir, 'quux')
1953 shutil.move(dst, dst_link)
1954 self.assertTrue(os.path.islink(dst_link))
1955 self.assertTrue(os.path.samefile(src, dst_link))
1956
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001957 def test_move_return_value(self):
1958 rv = shutil.move(self.src_file, self.dst_dir)
1959 self.assertEqual(rv,
1960 os.path.join(self.dst_dir, os.path.basename(self.src_file)))
1961
1962 def test_move_as_rename_return_value(self):
1963 rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar'))
1964 self.assertEqual(rv, os.path.join(self.dst_dir, 'bar'))
1965
R David Murray6ffface2014-06-11 14:40:13 -04001966 @mock_rename
1967 def test_move_file_special_function(self):
1968 moved = []
1969 def _copy(src, dst):
1970 moved.append((src, dst))
1971 shutil.move(self.src_file, self.dst_dir, copy_function=_copy)
1972 self.assertEqual(len(moved), 1)
1973
1974 @mock_rename
1975 def test_move_dir_special_function(self):
1976 moved = []
1977 def _copy(src, dst):
1978 moved.append((src, dst))
1979 support.create_empty_file(os.path.join(self.src_dir, 'child'))
1980 support.create_empty_file(os.path.join(self.src_dir, 'child1'))
1981 shutil.move(self.src_dir, self.dst_dir, copy_function=_copy)
1982 self.assertEqual(len(moved), 3)
1983
Tarek Ziadé5340db32010-04-19 22:30:51 +00001984
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001985class TestCopyFile(unittest.TestCase):
1986
1987 _delete = False
1988
1989 class Faux(object):
1990 _entered = False
1991 _exited_with = None
1992 _raised = False
1993 def __init__(self, raise_in_exit=False, suppress_at_exit=True):
1994 self._raise_in_exit = raise_in_exit
1995 self._suppress_at_exit = suppress_at_exit
1996 def read(self, *args):
1997 return ''
1998 def __enter__(self):
1999 self._entered = True
2000 def __exit__(self, exc_type, exc_val, exc_tb):
2001 self._exited_with = exc_type, exc_val, exc_tb
2002 if self._raise_in_exit:
2003 self._raised = True
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002004 raise OSError("Cannot close")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002005 return self._suppress_at_exit
2006
2007 def tearDown(self):
2008 if self._delete:
2009 del shutil.open
2010
2011 def _set_shutil_open(self, func):
2012 shutil.open = func
2013 self._delete = True
2014
2015 def test_w_source_open_fails(self):
2016 def _open(filename, mode='r'):
2017 if filename == 'srcfile':
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002018 raise OSError('Cannot open "srcfile"')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002019 assert 0 # shouldn't reach here.
2020
2021 self._set_shutil_open(_open)
2022
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002023 self.assertRaises(OSError, shutil.copyfile, 'srcfile', 'destfile')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002024
Victor Stinner937ee9e2018-06-26 02:11:06 +02002025 @unittest.skipIf(MACOS, "skipped on macOS")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002026 def test_w_dest_open_fails(self):
2027
2028 srcfile = self.Faux()
2029
2030 def _open(filename, mode='r'):
2031 if filename == 'srcfile':
2032 return srcfile
2033 if filename == 'destfile':
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002034 raise OSError('Cannot open "destfile"')
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002035 assert 0 # shouldn't reach here.
2036
2037 self._set_shutil_open(_open)
2038
2039 shutil.copyfile('srcfile', 'destfile')
2040 self.assertTrue(srcfile._entered)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002041 self.assertTrue(srcfile._exited_with[0] is OSError)
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002042 self.assertEqual(srcfile._exited_with[1].args,
2043 ('Cannot open "destfile"',))
2044
Victor Stinner937ee9e2018-06-26 02:11:06 +02002045 @unittest.skipIf(MACOS, "skipped on macOS")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002046 def test_w_dest_close_fails(self):
2047
2048 srcfile = self.Faux()
2049 destfile = self.Faux(True)
2050
2051 def _open(filename, mode='r'):
2052 if filename == 'srcfile':
2053 return srcfile
2054 if filename == 'destfile':
2055 return destfile
2056 assert 0 # shouldn't reach here.
2057
2058 self._set_shutil_open(_open)
2059
2060 shutil.copyfile('srcfile', 'destfile')
2061 self.assertTrue(srcfile._entered)
2062 self.assertTrue(destfile._entered)
2063 self.assertTrue(destfile._raised)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002064 self.assertTrue(srcfile._exited_with[0] is OSError)
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002065 self.assertEqual(srcfile._exited_with[1].args,
2066 ('Cannot close',))
2067
Victor Stinner937ee9e2018-06-26 02:11:06 +02002068 @unittest.skipIf(MACOS, "skipped on macOS")
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002069 def test_w_source_close_fails(self):
2070
2071 srcfile = self.Faux(True)
2072 destfile = self.Faux()
2073
2074 def _open(filename, mode='r'):
2075 if filename == 'srcfile':
2076 return srcfile
2077 if filename == 'destfile':
2078 return destfile
2079 assert 0 # shouldn't reach here.
2080
2081 self._set_shutil_open(_open)
2082
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002083 self.assertRaises(OSError,
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002084 shutil.copyfile, 'srcfile', 'destfile')
2085 self.assertTrue(srcfile._entered)
2086 self.assertTrue(destfile._entered)
2087 self.assertFalse(destfile._raised)
2088 self.assertTrue(srcfile._exited_with[0] is None)
2089 self.assertTrue(srcfile._raised)
2090
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002091 def test_move_dir_caseinsensitive(self):
2092 # Renames a folder to the same name
2093 # but a different case.
2094
2095 self.src_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07002096 self.addCleanup(shutil.rmtree, self.src_dir, True)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002097 dst_dir = os.path.join(
2098 os.path.dirname(self.src_dir),
2099 os.path.basename(self.src_dir).upper())
2100 self.assertNotEqual(self.src_dir, dst_dir)
2101
2102 try:
2103 shutil.move(self.src_dir, dst_dir)
2104 self.assertTrue(os.path.isdir(dst_dir))
2105 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +02002106 os.rmdir(dst_dir)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002107
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002108
Giampaolo Rodolac7f02a92018-06-19 08:27:29 -07002109class TestCopyFileObj(unittest.TestCase):
2110 FILESIZE = 2 * 1024 * 1024
2111
2112 @classmethod
2113 def setUpClass(cls):
2114 write_test_file(TESTFN, cls.FILESIZE)
2115
2116 @classmethod
2117 def tearDownClass(cls):
2118 support.unlink(TESTFN)
2119 support.unlink(TESTFN2)
2120
2121 def tearDown(self):
2122 support.unlink(TESTFN2)
2123
2124 @contextlib.contextmanager
2125 def get_files(self):
2126 with open(TESTFN, "rb") as src:
2127 with open(TESTFN2, "wb") as dst:
2128 yield (src, dst)
2129
2130 def assert_files_eq(self, src, dst):
2131 with open(src, 'rb') as fsrc:
2132 with open(dst, 'rb') as fdst:
2133 self.assertEqual(fsrc.read(), fdst.read())
2134
2135 def test_content(self):
2136 with self.get_files() as (src, dst):
2137 shutil.copyfileobj(src, dst)
2138 self.assert_files_eq(TESTFN, TESTFN2)
2139
2140 def test_file_not_closed(self):
2141 with self.get_files() as (src, dst):
2142 shutil.copyfileobj(src, dst)
2143 assert not src.closed
2144 assert not dst.closed
2145
2146 def test_file_offset(self):
2147 with self.get_files() as (src, dst):
2148 shutil.copyfileobj(src, dst)
2149 self.assertEqual(src.tell(), self.FILESIZE)
2150 self.assertEqual(dst.tell(), self.FILESIZE)
2151
2152 @unittest.skipIf(os.name != 'nt', "Windows only")
2153 def test_win_impl(self):
2154 # Make sure alternate Windows implementation is called.
2155 with unittest.mock.patch("shutil._copyfileobj_readinto") as m:
2156 shutil.copyfile(TESTFN, TESTFN2)
2157 assert m.called
2158
2159 # File size is 2 MiB but max buf size should be 1 MiB.
2160 self.assertEqual(m.call_args[0][2], 1 * 1024 * 1024)
2161
2162 # If file size < 1 MiB memoryview() length must be equal to
2163 # the actual file size.
2164 with tempfile.NamedTemporaryFile(delete=False) as f:
2165 f.write(b'foo')
2166 fname = f.name
2167 self.addCleanup(support.unlink, fname)
2168 with unittest.mock.patch("shutil._copyfileobj_readinto") as m:
2169 shutil.copyfile(fname, TESTFN2)
2170 self.assertEqual(m.call_args[0][2], 3)
2171
2172 # Empty files should not rely on readinto() variant.
2173 with tempfile.NamedTemporaryFile(delete=False) as f:
2174 pass
2175 fname = f.name
2176 self.addCleanup(support.unlink, fname)
2177 with unittest.mock.patch("shutil._copyfileobj_readinto") as m:
2178 shutil.copyfile(fname, TESTFN2)
2179 assert not m.called
2180 self.assert_files_eq(fname, TESTFN2)
2181
2182
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002183class _ZeroCopyFileTest(object):
2184 """Tests common to all zero-copy APIs."""
2185 FILESIZE = (10 * 1024 * 1024) # 10 MiB
2186 FILEDATA = b""
2187 PATCHPOINT = ""
2188
2189 @classmethod
2190 def setUpClass(cls):
2191 write_test_file(TESTFN, cls.FILESIZE)
2192 with open(TESTFN, 'rb') as f:
2193 cls.FILEDATA = f.read()
2194 assert len(cls.FILEDATA) == cls.FILESIZE
2195
2196 @classmethod
2197 def tearDownClass(cls):
2198 support.unlink(TESTFN)
2199
2200 def tearDown(self):
2201 support.unlink(TESTFN2)
2202
2203 @contextlib.contextmanager
2204 def get_files(self):
2205 with open(TESTFN, "rb") as src:
2206 with open(TESTFN2, "wb") as dst:
2207 yield (src, dst)
2208
2209 def zerocopy_fun(self, *args, **kwargs):
2210 raise NotImplementedError("must be implemented in subclass")
2211
2212 def reset(self):
2213 self.tearDown()
2214 self.tearDownClass()
2215 self.setUpClass()
2216 self.setUp()
2217
2218 # ---
2219
2220 def test_regular_copy(self):
2221 with self.get_files() as (src, dst):
2222 self.zerocopy_fun(src, dst)
2223 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2224 # Make sure the fallback function is not called.
2225 with self.get_files() as (src, dst):
2226 with unittest.mock.patch('shutil.copyfileobj') as m:
2227 shutil.copyfile(TESTFN, TESTFN2)
2228 assert not m.called
2229
2230 def test_same_file(self):
2231 self.addCleanup(self.reset)
2232 with self.get_files() as (src, dst):
2233 with self.assertRaises(Exception):
2234 self.zerocopy_fun(src, src)
2235 # Make sure src file is not corrupted.
2236 self.assertEqual(read_file(TESTFN, binary=True), self.FILEDATA)
2237
2238 def test_non_existent_src(self):
2239 name = tempfile.mktemp()
2240 with self.assertRaises(FileNotFoundError) as cm:
2241 shutil.copyfile(name, "new")
2242 self.assertEqual(cm.exception.filename, name)
2243
2244 def test_empty_file(self):
2245 srcname = TESTFN + 'src'
2246 dstname = TESTFN + 'dst'
2247 self.addCleanup(lambda: support.unlink(srcname))
2248 self.addCleanup(lambda: support.unlink(dstname))
2249 with open(srcname, "wb"):
2250 pass
2251
2252 with open(srcname, "rb") as src:
2253 with open(dstname, "wb") as dst:
2254 self.zerocopy_fun(src, dst)
2255
2256 self.assertEqual(read_file(dstname, binary=True), b"")
2257
2258 def test_unhandled_exception(self):
2259 with unittest.mock.patch(self.PATCHPOINT,
2260 side_effect=ZeroDivisionError):
2261 self.assertRaises(ZeroDivisionError,
2262 shutil.copyfile, TESTFN, TESTFN2)
2263
2264 def test_exception_on_first_call(self):
2265 # Emulate a case where the first call to the zero-copy
2266 # function raises an exception in which case the function is
2267 # supposed to give up immediately.
2268 with unittest.mock.patch(self.PATCHPOINT,
2269 side_effect=OSError(errno.EINVAL, "yo")):
2270 with self.get_files() as (src, dst):
2271 with self.assertRaises(_GiveupOnFastCopy):
2272 self.zerocopy_fun(src, dst)
2273
2274 def test_filesystem_full(self):
2275 # Emulate a case where filesystem is full and sendfile() fails
2276 # on first call.
2277 with unittest.mock.patch(self.PATCHPOINT,
2278 side_effect=OSError(errno.ENOSPC, "yo")):
2279 with self.get_files() as (src, dst):
2280 self.assertRaises(OSError, self.zerocopy_fun, src, dst)
2281
2282
2283@unittest.skipIf(not SUPPORTS_SENDFILE, 'os.sendfile() not supported')
2284class TestZeroCopySendfile(_ZeroCopyFileTest, unittest.TestCase):
2285 PATCHPOINT = "os.sendfile"
2286
2287 def zerocopy_fun(self, fsrc, fdst):
2288 return shutil._fastcopy_sendfile(fsrc, fdst)
2289
2290 def test_non_regular_file_src(self):
2291 with io.BytesIO(self.FILEDATA) as src:
2292 with open(TESTFN2, "wb") as dst:
2293 with self.assertRaises(_GiveupOnFastCopy):
2294 self.zerocopy_fun(src, dst)
2295 shutil.copyfileobj(src, dst)
2296
2297 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2298
2299 def test_non_regular_file_dst(self):
2300 with open(TESTFN, "rb") as src:
2301 with io.BytesIO() as dst:
2302 with self.assertRaises(_GiveupOnFastCopy):
2303 self.zerocopy_fun(src, dst)
2304 shutil.copyfileobj(src, dst)
2305 dst.seek(0)
2306 self.assertEqual(dst.read(), self.FILEDATA)
2307
2308 def test_exception_on_second_call(self):
2309 def sendfile(*args, **kwargs):
2310 if not flag:
2311 flag.append(None)
2312 return orig_sendfile(*args, **kwargs)
2313 else:
2314 raise OSError(errno.EBADF, "yo")
2315
2316 flag = []
2317 orig_sendfile = os.sendfile
2318 with unittest.mock.patch('os.sendfile', create=True,
2319 side_effect=sendfile):
2320 with self.get_files() as (src, dst):
2321 with self.assertRaises(OSError) as cm:
2322 shutil._fastcopy_sendfile(src, dst)
2323 assert flag
2324 self.assertEqual(cm.exception.errno, errno.EBADF)
2325
2326 def test_cant_get_size(self):
2327 # Emulate a case where src file size cannot be determined.
2328 # Internally bufsize will be set to a small value and
2329 # sendfile() will be called repeatedly.
2330 with unittest.mock.patch('os.fstat', side_effect=OSError) as m:
2331 with self.get_files() as (src, dst):
2332 shutil._fastcopy_sendfile(src, dst)
2333 assert m.called
2334 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2335
2336 def test_small_chunks(self):
2337 # Force internal file size detection to be smaller than the
2338 # actual file size. We want to force sendfile() to be called
2339 # multiple times, also in order to emulate a src fd which gets
2340 # bigger while it is being copied.
2341 mock = unittest.mock.Mock()
2342 mock.st_size = 65536 + 1
2343 with unittest.mock.patch('os.fstat', return_value=mock) as m:
2344 with self.get_files() as (src, dst):
2345 shutil._fastcopy_sendfile(src, dst)
2346 assert m.called
2347 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2348
2349 def test_big_chunk(self):
2350 # Force internal file size detection to be +100MB bigger than
2351 # the actual file size. Make sure sendfile() does not rely on
2352 # file size value except for (maybe) a better throughput /
2353 # performance.
2354 mock = unittest.mock.Mock()
2355 mock.st_size = self.FILESIZE + (100 * 1024 * 1024)
2356 with unittest.mock.patch('os.fstat', return_value=mock) as m:
2357 with self.get_files() as (src, dst):
2358 shutil._fastcopy_sendfile(src, dst)
2359 assert m.called
2360 self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA)
2361
2362 def test_blocksize_arg(self):
2363 with unittest.mock.patch('os.sendfile',
2364 side_effect=ZeroDivisionError) as m:
2365 self.assertRaises(ZeroDivisionError,
2366 shutil.copyfile, TESTFN, TESTFN2)
2367 blocksize = m.call_args[0][3]
2368 # Make sure file size and the block size arg passed to
2369 # sendfile() are the same.
2370 self.assertEqual(blocksize, os.path.getsize(TESTFN))
2371 # ...unless we're dealing with a small file.
2372 support.unlink(TESTFN2)
2373 write_file(TESTFN2, b"hello", binary=True)
2374 self.addCleanup(support.unlink, TESTFN2 + '3')
2375 self.assertRaises(ZeroDivisionError,
2376 shutil.copyfile, TESTFN2, TESTFN2 + '3')
2377 blocksize = m.call_args[0][3]
2378 self.assertEqual(blocksize, 2 ** 23)
2379
2380 def test_file2file_not_supported(self):
2381 # Emulate a case where sendfile() only support file->socket
2382 # fds. In such a case copyfile() is supposed to skip the
2383 # fast-copy attempt from then on.
Giampaolo Rodola413d9552019-05-30 14:05:41 +08002384 assert shutil._USE_CP_SENDFILE
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002385 try:
2386 with unittest.mock.patch(
2387 self.PATCHPOINT,
2388 side_effect=OSError(errno.ENOTSOCK, "yo")) as m:
2389 with self.get_files() as (src, dst):
2390 with self.assertRaises(_GiveupOnFastCopy):
2391 shutil._fastcopy_sendfile(src, dst)
2392 assert m.called
Giampaolo Rodola413d9552019-05-30 14:05:41 +08002393 assert not shutil._USE_CP_SENDFILE
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002394
2395 with unittest.mock.patch(self.PATCHPOINT) as m:
2396 shutil.copyfile(TESTFN, TESTFN2)
2397 assert not m.called
2398 finally:
Giampaolo Rodola413d9552019-05-30 14:05:41 +08002399 shutil._USE_CP_SENDFILE = True
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002400
2401
Victor Stinner937ee9e2018-06-26 02:11:06 +02002402@unittest.skipIf(not MACOS, 'macOS only')
Giampaolo Rodolac7f02a92018-06-19 08:27:29 -07002403class TestZeroCopyMACOS(_ZeroCopyFileTest, unittest.TestCase):
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002404 PATCHPOINT = "posix._fcopyfile"
2405
2406 def zerocopy_fun(self, src, dst):
Giampaolo Rodolac7f02a92018-06-19 08:27:29 -07002407 return shutil._fastcopy_fcopyfile(src, dst, posix._COPYFILE_DATA)
Giampaolo Rodola4a172cc2018-06-12 23:04:50 +02002408
2409
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002410class TermsizeTests(unittest.TestCase):
2411 def test_does_not_crash(self):
2412 """Check if get_terminal_size() returns a meaningful value.
2413
2414 There's no easy portable way to actually check the size of the
2415 terminal, so let's check if it returns something sensible instead.
2416 """
2417 size = shutil.get_terminal_size()
Antoine Pitroucfade362012-02-08 23:48:59 +01002418 self.assertGreaterEqual(size.columns, 0)
2419 self.assertGreaterEqual(size.lines, 0)
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002420
2421 def test_os_environ_first(self):
2422 "Check if environment variables have precedence"
2423
2424 with support.EnvironmentVarGuard() as env:
2425 env['COLUMNS'] = '777'
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002426 del env['LINES']
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002427 size = shutil.get_terminal_size()
2428 self.assertEqual(size.columns, 777)
2429
2430 with support.EnvironmentVarGuard() as env:
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002431 del env['COLUMNS']
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002432 env['LINES'] = '888'
2433 size = shutil.get_terminal_size()
2434 self.assertEqual(size.lines, 888)
2435
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002436 def test_bad_environ(self):
2437 with support.EnvironmentVarGuard() as env:
2438 env['COLUMNS'] = 'xxx'
2439 env['LINES'] = 'yyy'
2440 size = shutil.get_terminal_size()
2441 self.assertGreaterEqual(size.columns, 0)
2442 self.assertGreaterEqual(size.lines, 0)
2443
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002444 @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
Victor Stinner119ebb72016-04-19 22:24:56 +02002445 @unittest.skipUnless(hasattr(os, 'get_terminal_size'),
2446 'need os.get_terminal_size()')
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002447 def test_stty_match(self):
2448 """Check if stty returns the same results ignoring env
2449
2450 This test will fail if stdin and stdout are connected to
2451 different terminals with different sizes. Nevertheless, such
2452 situations should be pretty rare.
2453 """
2454 try:
2455 size = subprocess.check_output(['stty', 'size']).decode().split()
Xavier de Gaye38c8b7d2016-11-14 17:14:42 +01002456 except (FileNotFoundError, PermissionError,
2457 subprocess.CalledProcessError):
Antoine Pitroubcf2b592012-02-08 23:28:36 +01002458 self.skipTest("stty invocation failed")
2459 expected = (int(size[1]), int(size[0])) # reversed order
2460
2461 with support.EnvironmentVarGuard() as env:
2462 del env['LINES']
2463 del env['COLUMNS']
2464 actual = shutil.get_terminal_size()
2465
2466 self.assertEqual(expected, actual)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02002467
Serhiy Storchakad30829d2016-04-24 09:58:43 +03002468 def test_fallback(self):
2469 with support.EnvironmentVarGuard() as env:
2470 del env['LINES']
2471 del env['COLUMNS']
2472
2473 # sys.__stdout__ has no fileno()
2474 with support.swap_attr(sys, '__stdout__', None):
2475 size = shutil.get_terminal_size(fallback=(10, 20))
2476 self.assertEqual(size.columns, 10)
2477 self.assertEqual(size.lines, 20)
2478
2479 # sys.__stdout__ is not a terminal on Unix
2480 # or fileno() not in (0, 1, 2) on Windows
2481 with open(os.devnull, 'w') as f, \
2482 support.swap_attr(sys, '__stdout__', f):
2483 size = shutil.get_terminal_size(fallback=(30, 40))
2484 self.assertEqual(size.columns, 30)
2485 self.assertEqual(size.lines, 40)
2486
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00002487
Berker Peksag8083cd62014-11-01 11:04:06 +02002488class PublicAPITests(unittest.TestCase):
2489 """Ensures that the correct values are exposed in the public API."""
2490
2491 def test_module_all_attribute(self):
2492 self.assertTrue(hasattr(shutil, '__all__'))
2493 target_api = ['copyfileobj', 'copyfile', 'copymode', 'copystat',
2494 'copy', 'copy2', 'copytree', 'move', 'rmtree', 'Error',
2495 'SpecialFileError', 'ExecError', 'make_archive',
2496 'get_archive_formats', 'register_archive_format',
2497 'unregister_archive_format', 'get_unpack_formats',
2498 'register_unpack_format', 'unregister_unpack_format',
2499 'unpack_archive', 'ignore_patterns', 'chown', 'which',
2500 'get_terminal_size', 'SameFileError']
2501 if hasattr(os, 'statvfs') or os.name == 'nt':
2502 target_api.append('disk_usage')
2503 self.assertEqual(set(shutil.__all__), set(target_api))
2504
2505
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00002506if __name__ == '__main__':
Brett Cannon3e9a9ae2013-06-12 21:25:59 -04002507 unittest.main()