1# -*- coding: utf-8 -*- 2"""Tests for distutils.archive_util.""" 3import unittest 4import os 5import sys 6import tarfile 7from os.path import splitdrive 8import warnings 9 10from distutils import archive_util 11from distutils.archive_util import (check_archive_formats, make_tarball, 12 make_zipfile, make_archive, 13 ARCHIVE_FORMATS) 14from distutils.spawn import find_executable, spawn 15from distutils.tests import support 16from test.support import run_unittest, patch 17from .unix_compat import require_unix_id, require_uid_0, grp, pwd, UID_0_SUPPORT 18 19from .py38compat import change_cwd 20from .py38compat import check_warnings 21 22 23try: 24 import zipfile 25 ZIP_SUPPORT = True 26except ImportError: 27 ZIP_SUPPORT = find_executable('zip') 28 29try: 30 import zlib 31 ZLIB_SUPPORT = True 32except ImportError: 33 ZLIB_SUPPORT = False 34 35try: 36 import bz2 37except ImportError: 38 bz2 = None 39 40try: 41 import lzma 42except ImportError: 43 lzma = None 44 45def can_fs_encode(filename): 46 """ 47 Return True if the filename can be saved in the file system. 48 """ 49 if os.path.supports_unicode_filenames: 50 return True 51 try: 52 filename.encode(sys.getfilesystemencoding()) 53 except UnicodeEncodeError: 54 return False 55 return True 56 57 58class ArchiveUtilTestCase(support.TempdirManager, 59 support.LoggingSilencer, 60 unittest.TestCase): 61 62 @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') 63 def test_make_tarball(self, name='archive'): 64 # creating something to tar 65 tmpdir = self._create_files() 66 self._make_tarball(tmpdir, name, '.tar.gz') 67 # trying an uncompressed one 68 self._make_tarball(tmpdir, name, '.tar', compress=None) 69 70 @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') 71 def test_make_tarball_gzip(self): 72 tmpdir = self._create_files() 73 self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') 74 75 @unittest.skipUnless(bz2, 'Need bz2 support to run') 76 def test_make_tarball_bzip2(self): 77 tmpdir = self._create_files() 78 self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') 79 80 @unittest.skipUnless(lzma, 'Need lzma support to run') 81 def test_make_tarball_xz(self): 82 tmpdir = self._create_files() 83 self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') 84 85 @unittest.skipUnless(can_fs_encode('årchiv'), 86 'File system cannot handle this filename') 87 def test_make_tarball_latin1(self): 88 """ 89 Mirror test_make_tarball, except filename contains latin characters. 90 """ 91 self.test_make_tarball('årchiv') # note this isn't a real word 92 93 @unittest.skipUnless(can_fs_encode('のアーカイブ'), 94 'File system cannot handle this filename') 95 def test_make_tarball_extended(self): 96 """ 97 Mirror test_make_tarball, except filename contains extended 98 characters outside the latin charset. 99 """ 100 self.test_make_tarball('のアーカイブ') # japanese for archive 101 102 def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): 103 tmpdir2 = self.mkdtemp() 104 unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], 105 "source and target should be on same drive") 106 107 base_name = os.path.join(tmpdir2, target_name) 108 109 # working with relative paths to avoid tar warnings 110 with change_cwd(tmpdir): 111 make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) 112 113 # check if the compressed tarball was created 114 tarball = base_name + suffix 115 self.assertTrue(os.path.exists(tarball)) 116 self.assertEqual(self._tarinfo(tarball), self._created_files) 117 118 def _tarinfo(self, path): 119 tar = tarfile.open(path) 120 try: 121 names = tar.getnames() 122 names.sort() 123 return names 124 finally: 125 tar.close() 126 127 _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', 128 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] 129 _created_files = [p.rstrip('/') for p in _zip_created_files] 130 131 def _create_files(self): 132 # creating something to tar 133 tmpdir = self.mkdtemp() 134 dist = os.path.join(tmpdir, 'dist') 135 os.mkdir(dist) 136 self.write_file([dist, 'file1'], 'xxx') 137 self.write_file([dist, 'file2'], 'xxx') 138 os.mkdir(os.path.join(dist, 'sub')) 139 self.write_file([dist, 'sub', 'file3'], 'xxx') 140 os.mkdir(os.path.join(dist, 'sub2')) 141 return tmpdir 142 143 @unittest.skipUnless(find_executable('tar') and find_executable('gzip') 144 and ZLIB_SUPPORT, 145 'Need the tar, gzip and zlib command to run') 146 def test_tarfile_vs_tar(self): 147 tmpdir = self._create_files() 148 tmpdir2 = self.mkdtemp() 149 base_name = os.path.join(tmpdir2, 'archive') 150 old_dir = os.getcwd() 151 os.chdir(tmpdir) 152 try: 153 make_tarball(base_name, 'dist') 154 finally: 155 os.chdir(old_dir) 156 157 # check if the compressed tarball was created 158 tarball = base_name + '.tar.gz' 159 self.assertTrue(os.path.exists(tarball)) 160 161 # now create another tarball using `tar` 162 tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') 163 tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] 164 gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] 165 old_dir = os.getcwd() 166 os.chdir(tmpdir) 167 try: 168 spawn(tar_cmd) 169 spawn(gzip_cmd) 170 finally: 171 os.chdir(old_dir) 172 173 self.assertTrue(os.path.exists(tarball2)) 174 # let's compare both tarballs 175 self.assertEqual(self._tarinfo(tarball), self._created_files) 176 self.assertEqual(self._tarinfo(tarball2), self._created_files) 177 178 # trying an uncompressed one 179 base_name = os.path.join(tmpdir2, 'archive') 180 old_dir = os.getcwd() 181 os.chdir(tmpdir) 182 try: 183 make_tarball(base_name, 'dist', compress=None) 184 finally: 185 os.chdir(old_dir) 186 tarball = base_name + '.tar' 187 self.assertTrue(os.path.exists(tarball)) 188 189 # now for a dry_run 190 base_name = os.path.join(tmpdir2, 'archive') 191 old_dir = os.getcwd() 192 os.chdir(tmpdir) 193 try: 194 make_tarball(base_name, 'dist', compress=None, dry_run=True) 195 finally: 196 os.chdir(old_dir) 197 tarball = base_name + '.tar' 198 self.assertTrue(os.path.exists(tarball)) 199 200 @unittest.skipUnless(find_executable('compress'), 201 'The compress program is required') 202 def test_compress_deprecated(self): 203 tmpdir = self._create_files() 204 base_name = os.path.join(self.mkdtemp(), 'archive') 205 206 # using compress and testing the PendingDeprecationWarning 207 old_dir = os.getcwd() 208 os.chdir(tmpdir) 209 try: 210 with check_warnings() as w: 211 warnings.simplefilter("always") 212 make_tarball(base_name, 'dist', compress='compress') 213 finally: 214 os.chdir(old_dir) 215 tarball = base_name + '.tar.Z' 216 self.assertTrue(os.path.exists(tarball)) 217 self.assertEqual(len(w.warnings), 1) 218 219 # same test with dry_run 220 os.remove(tarball) 221 old_dir = os.getcwd() 222 os.chdir(tmpdir) 223 try: 224 with check_warnings() as w: 225 warnings.simplefilter("always") 226 make_tarball(base_name, 'dist', compress='compress', 227 dry_run=True) 228 finally: 229 os.chdir(old_dir) 230 self.assertFalse(os.path.exists(tarball)) 231 self.assertEqual(len(w.warnings), 1) 232 233 @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, 234 'Need zip and zlib support to run') 235 def test_make_zipfile(self): 236 # creating something to tar 237 tmpdir = self._create_files() 238 base_name = os.path.join(self.mkdtemp(), 'archive') 239 with change_cwd(tmpdir): 240 make_zipfile(base_name, 'dist') 241 242 # check if the compressed tarball was created 243 tarball = base_name + '.zip' 244 self.assertTrue(os.path.exists(tarball)) 245 with zipfile.ZipFile(tarball) as zf: 246 self.assertEqual(sorted(zf.namelist()), self._zip_created_files) 247 248 @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') 249 def test_make_zipfile_no_zlib(self): 250 patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError 251 252 called = [] 253 zipfile_class = zipfile.ZipFile 254 def fake_zipfile(*a, **kw): 255 if kw.get('compression', None) == zipfile.ZIP_STORED: 256 called.append((a, kw)) 257 return zipfile_class(*a, **kw) 258 259 patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) 260 261 # create something to tar and compress 262 tmpdir = self._create_files() 263 base_name = os.path.join(self.mkdtemp(), 'archive') 264 with change_cwd(tmpdir): 265 make_zipfile(base_name, 'dist') 266 267 tarball = base_name + '.zip' 268 self.assertEqual(called, 269 [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) 270 self.assertTrue(os.path.exists(tarball)) 271 with zipfile.ZipFile(tarball) as zf: 272 self.assertEqual(sorted(zf.namelist()), self._zip_created_files) 273 274 def test_check_archive_formats(self): 275 self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), 276 'xxx') 277 self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', 278 'ztar', 'tar', 'zip'])) 279 280 def test_make_archive(self): 281 tmpdir = self.mkdtemp() 282 base_name = os.path.join(tmpdir, 'archive') 283 self.assertRaises(ValueError, make_archive, base_name, 'xxx') 284 285 def test_make_archive_cwd(self): 286 current_dir = os.getcwd() 287 def _breaks(*args, **kw): 288 raise RuntimeError() 289 ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') 290 try: 291 try: 292 make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) 293 except: 294 pass 295 self.assertEqual(os.getcwd(), current_dir) 296 finally: 297 del ARCHIVE_FORMATS['xxx'] 298 299 def test_make_archive_tar(self): 300 base_dir = self._create_files() 301 base_name = os.path.join(self.mkdtemp() , 'archive') 302 res = make_archive(base_name, 'tar', base_dir, 'dist') 303 self.assertTrue(os.path.exists(res)) 304 self.assertEqual(os.path.basename(res), 'archive.tar') 305 self.assertEqual(self._tarinfo(res), self._created_files) 306 307 @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') 308 def test_make_archive_gztar(self): 309 base_dir = self._create_files() 310 base_name = os.path.join(self.mkdtemp() , 'archive') 311 res = make_archive(base_name, 'gztar', base_dir, 'dist') 312 self.assertTrue(os.path.exists(res)) 313 self.assertEqual(os.path.basename(res), 'archive.tar.gz') 314 self.assertEqual(self._tarinfo(res), self._created_files) 315 316 @unittest.skipUnless(bz2, 'Need bz2 support to run') 317 def test_make_archive_bztar(self): 318 base_dir = self._create_files() 319 base_name = os.path.join(self.mkdtemp() , 'archive') 320 res = make_archive(base_name, 'bztar', base_dir, 'dist') 321 self.assertTrue(os.path.exists(res)) 322 self.assertEqual(os.path.basename(res), 'archive.tar.bz2') 323 self.assertEqual(self._tarinfo(res), self._created_files) 324 325 @unittest.skipUnless(lzma, 'Need xz support to run') 326 def test_make_archive_xztar(self): 327 base_dir = self._create_files() 328 base_name = os.path.join(self.mkdtemp() , 'archive') 329 res = make_archive(base_name, 'xztar', base_dir, 'dist') 330 self.assertTrue(os.path.exists(res)) 331 self.assertEqual(os.path.basename(res), 'archive.tar.xz') 332 self.assertEqual(self._tarinfo(res), self._created_files) 333 334 def test_make_archive_owner_group(self): 335 # testing make_archive with owner and group, with various combinations 336 # this works even if there's not gid/uid support 337 if UID_0_SUPPORT: 338 group = grp.getgrgid(0)[0] 339 owner = pwd.getpwuid(0)[0] 340 else: 341 group = owner = 'root' 342 343 base_dir = self._create_files() 344 root_dir = self.mkdtemp() 345 base_name = os.path.join(self.mkdtemp() , 'archive') 346 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, 347 group=group) 348 self.assertTrue(os.path.exists(res)) 349 350 res = make_archive(base_name, 'zip', root_dir, base_dir) 351 self.assertTrue(os.path.exists(res)) 352 353 res = make_archive(base_name, 'tar', root_dir, base_dir, 354 owner=owner, group=group) 355 self.assertTrue(os.path.exists(res)) 356 357 res = make_archive(base_name, 'tar', root_dir, base_dir, 358 owner='kjhkjhkjg', group='oihohoh') 359 self.assertTrue(os.path.exists(res)) 360 361 @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") 362 @require_unix_id 363 @require_uid_0 364 def test_tarfile_root_owner(self): 365 tmpdir = self._create_files() 366 base_name = os.path.join(self.mkdtemp(), 'archive') 367 old_dir = os.getcwd() 368 os.chdir(tmpdir) 369 group = grp.getgrgid(0)[0] 370 owner = pwd.getpwuid(0)[0] 371 try: 372 archive_name = make_tarball(base_name, 'dist', compress=None, 373 owner=owner, group=group) 374 finally: 375 os.chdir(old_dir) 376 377 # check if the compressed tarball was created 378 self.assertTrue(os.path.exists(archive_name)) 379 380 # now checks the rights 381 archive = tarfile.open(archive_name) 382 try: 383 for member in archive.getmembers(): 384 self.assertEqual(member.uid, 0) 385 self.assertEqual(member.gid, 0) 386 finally: 387 archive.close() 388 389def test_suite(): 390 return unittest.TestLoader().loadTestsFromTestCase(ArchiveUtilTestCase) 391 392if __name__ == "__main__": 393 run_unittest(test_suite()) 394