17db96d56Sopenharmony_ciimport compileall 27db96d56Sopenharmony_ciimport contextlib 37db96d56Sopenharmony_ciimport filecmp 47db96d56Sopenharmony_ciimport importlib.util 57db96d56Sopenharmony_ciimport io 67db96d56Sopenharmony_ciimport os 77db96d56Sopenharmony_ciimport pathlib 87db96d56Sopenharmony_ciimport py_compile 97db96d56Sopenharmony_ciimport shutil 107db96d56Sopenharmony_ciimport struct 117db96d56Sopenharmony_ciimport sys 127db96d56Sopenharmony_ciimport tempfile 137db96d56Sopenharmony_ciimport test.test_importlib.util 147db96d56Sopenharmony_ciimport time 157db96d56Sopenharmony_ciimport unittest 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_cifrom unittest import mock, skipUnless 187db96d56Sopenharmony_citry: 197db96d56Sopenharmony_ci # compileall relies on ProcessPoolExecutor if ProcessPoolExecutor exists 207db96d56Sopenharmony_ci # and it can function. 217db96d56Sopenharmony_ci from concurrent.futures import ProcessPoolExecutor 227db96d56Sopenharmony_ci from concurrent.futures.process import _check_system_limits 237db96d56Sopenharmony_ci _check_system_limits() 247db96d56Sopenharmony_ci _have_multiprocessing = True 257db96d56Sopenharmony_ciexcept (NotImplementedError, ModuleNotFoundError): 267db96d56Sopenharmony_ci _have_multiprocessing = False 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_cifrom test import support 297db96d56Sopenharmony_cifrom test.support import os_helper 307db96d56Sopenharmony_cifrom test.support import script_helper 317db96d56Sopenharmony_cifrom test.test_py_compile import without_source_date_epoch 327db96d56Sopenharmony_cifrom test.test_py_compile import SourceDateEpochTestMeta 337db96d56Sopenharmony_ci 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_cidef get_pyc(script, opt): 367db96d56Sopenharmony_ci if not opt: 377db96d56Sopenharmony_ci # Replace None and 0 with '' 387db96d56Sopenharmony_ci opt = '' 397db96d56Sopenharmony_ci return importlib.util.cache_from_source(script, optimization=opt) 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci 427db96d56Sopenharmony_cidef get_pycs(script): 437db96d56Sopenharmony_ci return [get_pyc(script, opt) for opt in (0, 1, 2)] 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_cidef is_hardlink(filename1, filename2): 477db96d56Sopenharmony_ci """Returns True if two files have the same inode (hardlink)""" 487db96d56Sopenharmony_ci inode1 = os.stat(filename1).st_ino 497db96d56Sopenharmony_ci inode2 = os.stat(filename2).st_ino 507db96d56Sopenharmony_ci return inode1 == inode2 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_ci 537db96d56Sopenharmony_ciclass CompileallTestsBase: 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci def setUp(self): 567db96d56Sopenharmony_ci self.directory = tempfile.mkdtemp() 577db96d56Sopenharmony_ci self.source_path = os.path.join(self.directory, '_test.py') 587db96d56Sopenharmony_ci self.bc_path = importlib.util.cache_from_source(self.source_path) 597db96d56Sopenharmony_ci with open(self.source_path, 'w', encoding="utf-8") as file: 607db96d56Sopenharmony_ci file.write('x = 123\n') 617db96d56Sopenharmony_ci self.source_path2 = os.path.join(self.directory, '_test2.py') 627db96d56Sopenharmony_ci self.bc_path2 = importlib.util.cache_from_source(self.source_path2) 637db96d56Sopenharmony_ci shutil.copyfile(self.source_path, self.source_path2) 647db96d56Sopenharmony_ci self.subdirectory = os.path.join(self.directory, '_subdir') 657db96d56Sopenharmony_ci os.mkdir(self.subdirectory) 667db96d56Sopenharmony_ci self.source_path3 = os.path.join(self.subdirectory, '_test3.py') 677db96d56Sopenharmony_ci shutil.copyfile(self.source_path, self.source_path3) 687db96d56Sopenharmony_ci 697db96d56Sopenharmony_ci def tearDown(self): 707db96d56Sopenharmony_ci shutil.rmtree(self.directory) 717db96d56Sopenharmony_ci 727db96d56Sopenharmony_ci def add_bad_source_file(self): 737db96d56Sopenharmony_ci self.bad_source_path = os.path.join(self.directory, '_test_bad.py') 747db96d56Sopenharmony_ci with open(self.bad_source_path, 'w', encoding="utf-8") as file: 757db96d56Sopenharmony_ci file.write('x (\n') 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci def timestamp_metadata(self): 787db96d56Sopenharmony_ci with open(self.bc_path, 'rb') as file: 797db96d56Sopenharmony_ci data = file.read(12) 807db96d56Sopenharmony_ci mtime = int(os.stat(self.source_path).st_mtime) 817db96d56Sopenharmony_ci compare = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, 0, 827db96d56Sopenharmony_ci mtime & 0xFFFF_FFFF) 837db96d56Sopenharmony_ci return data, compare 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci def test_year_2038_mtime_compilation(self): 867db96d56Sopenharmony_ci # Test to make sure we can handle mtimes larger than what a 32-bit 877db96d56Sopenharmony_ci # signed number can hold as part of bpo-34990 887db96d56Sopenharmony_ci try: 897db96d56Sopenharmony_ci os.utime(self.source_path, (2**32 - 1, 2**32 - 1)) 907db96d56Sopenharmony_ci except (OverflowError, OSError): 917db96d56Sopenharmony_ci self.skipTest("filesystem doesn't support timestamps near 2**32") 927db96d56Sopenharmony_ci with contextlib.redirect_stdout(io.StringIO()): 937db96d56Sopenharmony_ci self.assertTrue(compileall.compile_file(self.source_path)) 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci def test_larger_than_32_bit_times(self): 967db96d56Sopenharmony_ci # This is similar to the test above but we skip it if the OS doesn't 977db96d56Sopenharmony_ci # support modification times larger than 32-bits. 987db96d56Sopenharmony_ci try: 997db96d56Sopenharmony_ci os.utime(self.source_path, (2**35, 2**35)) 1007db96d56Sopenharmony_ci except (OverflowError, OSError): 1017db96d56Sopenharmony_ci self.skipTest("filesystem doesn't support large timestamps") 1027db96d56Sopenharmony_ci with contextlib.redirect_stdout(io.StringIO()): 1037db96d56Sopenharmony_ci self.assertTrue(compileall.compile_file(self.source_path)) 1047db96d56Sopenharmony_ci 1057db96d56Sopenharmony_ci def recreation_check(self, metadata): 1067db96d56Sopenharmony_ci """Check that compileall recreates bytecode when the new metadata is 1077db96d56Sopenharmony_ci used.""" 1087db96d56Sopenharmony_ci if os.environ.get('SOURCE_DATE_EPOCH'): 1097db96d56Sopenharmony_ci raise unittest.SkipTest('SOURCE_DATE_EPOCH is set') 1107db96d56Sopenharmony_ci py_compile.compile(self.source_path) 1117db96d56Sopenharmony_ci self.assertEqual(*self.timestamp_metadata()) 1127db96d56Sopenharmony_ci with open(self.bc_path, 'rb') as file: 1137db96d56Sopenharmony_ci bc = file.read()[len(metadata):] 1147db96d56Sopenharmony_ci with open(self.bc_path, 'wb') as file: 1157db96d56Sopenharmony_ci file.write(metadata) 1167db96d56Sopenharmony_ci file.write(bc) 1177db96d56Sopenharmony_ci self.assertNotEqual(*self.timestamp_metadata()) 1187db96d56Sopenharmony_ci compileall.compile_dir(self.directory, force=False, quiet=True) 1197db96d56Sopenharmony_ci self.assertTrue(*self.timestamp_metadata()) 1207db96d56Sopenharmony_ci 1217db96d56Sopenharmony_ci def test_mtime(self): 1227db96d56Sopenharmony_ci # Test a change in mtime leads to a new .pyc. 1237db96d56Sopenharmony_ci self.recreation_check(struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, 1247db96d56Sopenharmony_ci 0, 1)) 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci def test_magic_number(self): 1277db96d56Sopenharmony_ci # Test a change in mtime leads to a new .pyc. 1287db96d56Sopenharmony_ci self.recreation_check(b'\0\0\0\0') 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ci def test_compile_files(self): 1317db96d56Sopenharmony_ci # Test compiling a single file, and complete directory 1327db96d56Sopenharmony_ci for fn in (self.bc_path, self.bc_path2): 1337db96d56Sopenharmony_ci try: 1347db96d56Sopenharmony_ci os.unlink(fn) 1357db96d56Sopenharmony_ci except: 1367db96d56Sopenharmony_ci pass 1377db96d56Sopenharmony_ci self.assertTrue(compileall.compile_file(self.source_path, 1387db96d56Sopenharmony_ci force=False, quiet=True)) 1397db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path) and 1407db96d56Sopenharmony_ci not os.path.isfile(self.bc_path2)) 1417db96d56Sopenharmony_ci os.unlink(self.bc_path) 1427db96d56Sopenharmony_ci self.assertTrue(compileall.compile_dir(self.directory, force=False, 1437db96d56Sopenharmony_ci quiet=True)) 1447db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path) and 1457db96d56Sopenharmony_ci os.path.isfile(self.bc_path2)) 1467db96d56Sopenharmony_ci os.unlink(self.bc_path) 1477db96d56Sopenharmony_ci os.unlink(self.bc_path2) 1487db96d56Sopenharmony_ci # Test against bad files 1497db96d56Sopenharmony_ci self.add_bad_source_file() 1507db96d56Sopenharmony_ci self.assertFalse(compileall.compile_file(self.bad_source_path, 1517db96d56Sopenharmony_ci force=False, quiet=2)) 1527db96d56Sopenharmony_ci self.assertFalse(compileall.compile_dir(self.directory, 1537db96d56Sopenharmony_ci force=False, quiet=2)) 1547db96d56Sopenharmony_ci 1557db96d56Sopenharmony_ci def test_compile_file_pathlike(self): 1567db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(self.bc_path)) 1577db96d56Sopenharmony_ci # we should also test the output 1587db96d56Sopenharmony_ci with support.captured_stdout() as stdout: 1597db96d56Sopenharmony_ci self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path))) 1607db96d56Sopenharmony_ci self.assertRegex(stdout.getvalue(), r'Compiling ([^WindowsPath|PosixPath].*)') 1617db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path)) 1627db96d56Sopenharmony_ci 1637db96d56Sopenharmony_ci def test_compile_file_pathlike_ddir(self): 1647db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(self.bc_path)) 1657db96d56Sopenharmony_ci self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path), 1667db96d56Sopenharmony_ci ddir=pathlib.Path('ddir_path'), 1677db96d56Sopenharmony_ci quiet=2)) 1687db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path)) 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci def test_compile_file_pathlike_stripdir(self): 1717db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(self.bc_path)) 1727db96d56Sopenharmony_ci self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path), 1737db96d56Sopenharmony_ci stripdir=pathlib.Path('stripdir_path'), 1747db96d56Sopenharmony_ci quiet=2)) 1757db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path)) 1767db96d56Sopenharmony_ci 1777db96d56Sopenharmony_ci def test_compile_file_pathlike_prependdir(self): 1787db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(self.bc_path)) 1797db96d56Sopenharmony_ci self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path), 1807db96d56Sopenharmony_ci prependdir=pathlib.Path('prependdir_path'), 1817db96d56Sopenharmony_ci quiet=2)) 1827db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path)) 1837db96d56Sopenharmony_ci 1847db96d56Sopenharmony_ci def test_compile_path(self): 1857db96d56Sopenharmony_ci with test.test_importlib.util.import_state(path=[self.directory]): 1867db96d56Sopenharmony_ci self.assertTrue(compileall.compile_path(quiet=2)) 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci with test.test_importlib.util.import_state(path=[self.directory]): 1897db96d56Sopenharmony_ci self.add_bad_source_file() 1907db96d56Sopenharmony_ci self.assertFalse(compileall.compile_path(skip_curdir=False, 1917db96d56Sopenharmony_ci force=True, quiet=2)) 1927db96d56Sopenharmony_ci 1937db96d56Sopenharmony_ci def test_no_pycache_in_non_package(self): 1947db96d56Sopenharmony_ci # Bug 8563 reported that __pycache__ directories got created by 1957db96d56Sopenharmony_ci # compile_file() for non-.py files. 1967db96d56Sopenharmony_ci data_dir = os.path.join(self.directory, 'data') 1977db96d56Sopenharmony_ci data_file = os.path.join(data_dir, 'file') 1987db96d56Sopenharmony_ci os.mkdir(data_dir) 1997db96d56Sopenharmony_ci # touch data/file 2007db96d56Sopenharmony_ci with open(data_file, 'wb'): 2017db96d56Sopenharmony_ci pass 2027db96d56Sopenharmony_ci compileall.compile_file(data_file) 2037db96d56Sopenharmony_ci self.assertFalse(os.path.exists(os.path.join(data_dir, '__pycache__'))) 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ci def test_compile_file_encoding_fallback(self): 2077db96d56Sopenharmony_ci # Bug 44666 reported that compile_file failed when sys.stdout.encoding is None 2087db96d56Sopenharmony_ci self.add_bad_source_file() 2097db96d56Sopenharmony_ci with contextlib.redirect_stdout(io.StringIO()): 2107db96d56Sopenharmony_ci self.assertFalse(compileall.compile_file(self.bad_source_path)) 2117db96d56Sopenharmony_ci 2127db96d56Sopenharmony_ci 2137db96d56Sopenharmony_ci def test_optimize(self): 2147db96d56Sopenharmony_ci # make sure compiling with different optimization settings than the 2157db96d56Sopenharmony_ci # interpreter's creates the correct file names 2167db96d56Sopenharmony_ci optimize, opt = (1, 1) if __debug__ else (0, '') 2177db96d56Sopenharmony_ci compileall.compile_dir(self.directory, quiet=True, optimize=optimize) 2187db96d56Sopenharmony_ci cached = importlib.util.cache_from_source(self.source_path, 2197db96d56Sopenharmony_ci optimization=opt) 2207db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(cached)) 2217db96d56Sopenharmony_ci cached2 = importlib.util.cache_from_source(self.source_path2, 2227db96d56Sopenharmony_ci optimization=opt) 2237db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(cached2)) 2247db96d56Sopenharmony_ci cached3 = importlib.util.cache_from_source(self.source_path3, 2257db96d56Sopenharmony_ci optimization=opt) 2267db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(cached3)) 2277db96d56Sopenharmony_ci 2287db96d56Sopenharmony_ci def test_compile_dir_pathlike(self): 2297db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(self.bc_path)) 2307db96d56Sopenharmony_ci with support.captured_stdout() as stdout: 2317db96d56Sopenharmony_ci compileall.compile_dir(pathlib.Path(self.directory)) 2327db96d56Sopenharmony_ci line = stdout.getvalue().splitlines()[0] 2337db96d56Sopenharmony_ci self.assertRegex(line, r'Listing ([^WindowsPath|PosixPath].*)') 2347db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path)) 2357db96d56Sopenharmony_ci 2367db96d56Sopenharmony_ci def test_compile_dir_pathlike_stripdir(self): 2377db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(self.bc_path)) 2387db96d56Sopenharmony_ci self.assertTrue(compileall.compile_dir(pathlib.Path(self.directory), 2397db96d56Sopenharmony_ci stripdir=pathlib.Path('stripdir_path'), 2407db96d56Sopenharmony_ci quiet=2)) 2417db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path)) 2427db96d56Sopenharmony_ci 2437db96d56Sopenharmony_ci def test_compile_dir_pathlike_prependdir(self): 2447db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(self.bc_path)) 2457db96d56Sopenharmony_ci self.assertTrue(compileall.compile_dir(pathlib.Path(self.directory), 2467db96d56Sopenharmony_ci prependdir=pathlib.Path('prependdir_path'), 2477db96d56Sopenharmony_ci quiet=2)) 2487db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(self.bc_path)) 2497db96d56Sopenharmony_ci 2507db96d56Sopenharmony_ci @skipUnless(_have_multiprocessing, "requires multiprocessing") 2517db96d56Sopenharmony_ci @mock.patch('concurrent.futures.ProcessPoolExecutor') 2527db96d56Sopenharmony_ci def test_compile_pool_called(self, pool_mock): 2537db96d56Sopenharmony_ci compileall.compile_dir(self.directory, quiet=True, workers=5) 2547db96d56Sopenharmony_ci self.assertTrue(pool_mock.called) 2557db96d56Sopenharmony_ci 2567db96d56Sopenharmony_ci def test_compile_workers_non_positive(self): 2577db96d56Sopenharmony_ci with self.assertRaisesRegex(ValueError, 2587db96d56Sopenharmony_ci "workers must be greater or equal to 0"): 2597db96d56Sopenharmony_ci compileall.compile_dir(self.directory, workers=-1) 2607db96d56Sopenharmony_ci 2617db96d56Sopenharmony_ci @skipUnless(_have_multiprocessing, "requires multiprocessing") 2627db96d56Sopenharmony_ci @mock.patch('concurrent.futures.ProcessPoolExecutor') 2637db96d56Sopenharmony_ci def test_compile_workers_cpu_count(self, pool_mock): 2647db96d56Sopenharmony_ci compileall.compile_dir(self.directory, quiet=True, workers=0) 2657db96d56Sopenharmony_ci self.assertEqual(pool_mock.call_args[1]['max_workers'], None) 2667db96d56Sopenharmony_ci 2677db96d56Sopenharmony_ci @skipUnless(_have_multiprocessing, "requires multiprocessing") 2687db96d56Sopenharmony_ci @mock.patch('concurrent.futures.ProcessPoolExecutor') 2697db96d56Sopenharmony_ci @mock.patch('compileall.compile_file') 2707db96d56Sopenharmony_ci def test_compile_one_worker(self, compile_file_mock, pool_mock): 2717db96d56Sopenharmony_ci compileall.compile_dir(self.directory, quiet=True) 2727db96d56Sopenharmony_ci self.assertFalse(pool_mock.called) 2737db96d56Sopenharmony_ci self.assertTrue(compile_file_mock.called) 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ci @skipUnless(_have_multiprocessing, "requires multiprocessing") 2767db96d56Sopenharmony_ci @mock.patch('concurrent.futures.ProcessPoolExecutor', new=None) 2777db96d56Sopenharmony_ci @mock.patch('compileall.compile_file') 2787db96d56Sopenharmony_ci def test_compile_missing_multiprocessing(self, compile_file_mock): 2797db96d56Sopenharmony_ci compileall.compile_dir(self.directory, quiet=True, workers=5) 2807db96d56Sopenharmony_ci self.assertTrue(compile_file_mock.called) 2817db96d56Sopenharmony_ci 2827db96d56Sopenharmony_ci def test_compile_dir_maxlevels(self): 2837db96d56Sopenharmony_ci # Test the actual impact of maxlevels parameter 2847db96d56Sopenharmony_ci depth = 3 2857db96d56Sopenharmony_ci path = self.directory 2867db96d56Sopenharmony_ci for i in range(1, depth + 1): 2877db96d56Sopenharmony_ci path = os.path.join(path, f"dir_{i}") 2887db96d56Sopenharmony_ci source = os.path.join(path, 'script.py') 2897db96d56Sopenharmony_ci os.mkdir(path) 2907db96d56Sopenharmony_ci shutil.copyfile(self.source_path, source) 2917db96d56Sopenharmony_ci pyc_filename = importlib.util.cache_from_source(source) 2927db96d56Sopenharmony_ci 2937db96d56Sopenharmony_ci compileall.compile_dir(self.directory, quiet=True, maxlevels=depth - 1) 2947db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(pyc_filename)) 2957db96d56Sopenharmony_ci 2967db96d56Sopenharmony_ci compileall.compile_dir(self.directory, quiet=True, maxlevels=depth) 2977db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(pyc_filename)) 2987db96d56Sopenharmony_ci 2997db96d56Sopenharmony_ci def _test_ddir_only(self, *, ddir, parallel=True): 3007db96d56Sopenharmony_ci """Recursive compile_dir ddir must contain package paths; bpo39769.""" 3017db96d56Sopenharmony_ci fullpath = ["test", "foo"] 3027db96d56Sopenharmony_ci path = self.directory 3037db96d56Sopenharmony_ci mods = [] 3047db96d56Sopenharmony_ci for subdir in fullpath: 3057db96d56Sopenharmony_ci path = os.path.join(path, subdir) 3067db96d56Sopenharmony_ci os.mkdir(path) 3077db96d56Sopenharmony_ci script_helper.make_script(path, "__init__", "") 3087db96d56Sopenharmony_ci mods.append(script_helper.make_script(path, "mod", 3097db96d56Sopenharmony_ci "def fn(): 1/0\nfn()\n")) 3107db96d56Sopenharmony_ci compileall.compile_dir( 3117db96d56Sopenharmony_ci self.directory, quiet=True, ddir=ddir, 3127db96d56Sopenharmony_ci workers=2 if parallel else 1) 3137db96d56Sopenharmony_ci self.assertTrue(mods) 3147db96d56Sopenharmony_ci for mod in mods: 3157db96d56Sopenharmony_ci self.assertTrue(mod.startswith(self.directory), mod) 3167db96d56Sopenharmony_ci modcode = importlib.util.cache_from_source(mod) 3177db96d56Sopenharmony_ci modpath = mod[len(self.directory+os.sep):] 3187db96d56Sopenharmony_ci _, _, err = script_helper.assert_python_failure(modcode) 3197db96d56Sopenharmony_ci expected_in = os.path.join(ddir, modpath) 3207db96d56Sopenharmony_ci mod_code_obj = test.test_importlib.util.get_code_from_pyc(modcode) 3217db96d56Sopenharmony_ci self.assertEqual(mod_code_obj.co_filename, expected_in) 3227db96d56Sopenharmony_ci self.assertIn(f'"{expected_in}"', os.fsdecode(err)) 3237db96d56Sopenharmony_ci 3247db96d56Sopenharmony_ci def test_ddir_only_one_worker(self): 3257db96d56Sopenharmony_ci """Recursive compile_dir ddir= contains package paths; bpo39769.""" 3267db96d56Sopenharmony_ci return self._test_ddir_only(ddir="<a prefix>", parallel=False) 3277db96d56Sopenharmony_ci 3287db96d56Sopenharmony_ci @skipUnless(_have_multiprocessing, "requires multiprocessing") 3297db96d56Sopenharmony_ci def test_ddir_multiple_workers(self): 3307db96d56Sopenharmony_ci """Recursive compile_dir ddir= contains package paths; bpo39769.""" 3317db96d56Sopenharmony_ci return self._test_ddir_only(ddir="<a prefix>", parallel=True) 3327db96d56Sopenharmony_ci 3337db96d56Sopenharmony_ci def test_ddir_empty_only_one_worker(self): 3347db96d56Sopenharmony_ci """Recursive compile_dir ddir='' contains package paths; bpo39769.""" 3357db96d56Sopenharmony_ci return self._test_ddir_only(ddir="", parallel=False) 3367db96d56Sopenharmony_ci 3377db96d56Sopenharmony_ci @skipUnless(_have_multiprocessing, "requires multiprocessing") 3387db96d56Sopenharmony_ci def test_ddir_empty_multiple_workers(self): 3397db96d56Sopenharmony_ci """Recursive compile_dir ddir='' contains package paths; bpo39769.""" 3407db96d56Sopenharmony_ci return self._test_ddir_only(ddir="", parallel=True) 3417db96d56Sopenharmony_ci 3427db96d56Sopenharmony_ci def test_strip_only(self): 3437db96d56Sopenharmony_ci fullpath = ["test", "build", "real", "path"] 3447db96d56Sopenharmony_ci path = os.path.join(self.directory, *fullpath) 3457db96d56Sopenharmony_ci os.makedirs(path) 3467db96d56Sopenharmony_ci script = script_helper.make_script(path, "test", "1 / 0") 3477db96d56Sopenharmony_ci bc = importlib.util.cache_from_source(script) 3487db96d56Sopenharmony_ci stripdir = os.path.join(self.directory, *fullpath[:2]) 3497db96d56Sopenharmony_ci compileall.compile_dir(path, quiet=True, stripdir=stripdir) 3507db96d56Sopenharmony_ci rc, out, err = script_helper.assert_python_failure(bc) 3517db96d56Sopenharmony_ci expected_in = os.path.join(*fullpath[2:]) 3527db96d56Sopenharmony_ci self.assertIn( 3537db96d56Sopenharmony_ci expected_in, 3547db96d56Sopenharmony_ci str(err, encoding=sys.getdefaultencoding()) 3557db96d56Sopenharmony_ci ) 3567db96d56Sopenharmony_ci self.assertNotIn( 3577db96d56Sopenharmony_ci stripdir, 3587db96d56Sopenharmony_ci str(err, encoding=sys.getdefaultencoding()) 3597db96d56Sopenharmony_ci ) 3607db96d56Sopenharmony_ci 3617db96d56Sopenharmony_ci def test_prepend_only(self): 3627db96d56Sopenharmony_ci fullpath = ["test", "build", "real", "path"] 3637db96d56Sopenharmony_ci path = os.path.join(self.directory, *fullpath) 3647db96d56Sopenharmony_ci os.makedirs(path) 3657db96d56Sopenharmony_ci script = script_helper.make_script(path, "test", "1 / 0") 3667db96d56Sopenharmony_ci bc = importlib.util.cache_from_source(script) 3677db96d56Sopenharmony_ci prependdir = "/foo" 3687db96d56Sopenharmony_ci compileall.compile_dir(path, quiet=True, prependdir=prependdir) 3697db96d56Sopenharmony_ci rc, out, err = script_helper.assert_python_failure(bc) 3707db96d56Sopenharmony_ci expected_in = os.path.join(prependdir, self.directory, *fullpath) 3717db96d56Sopenharmony_ci self.assertIn( 3727db96d56Sopenharmony_ci expected_in, 3737db96d56Sopenharmony_ci str(err, encoding=sys.getdefaultencoding()) 3747db96d56Sopenharmony_ci ) 3757db96d56Sopenharmony_ci 3767db96d56Sopenharmony_ci def test_strip_and_prepend(self): 3777db96d56Sopenharmony_ci fullpath = ["test", "build", "real", "path"] 3787db96d56Sopenharmony_ci path = os.path.join(self.directory, *fullpath) 3797db96d56Sopenharmony_ci os.makedirs(path) 3807db96d56Sopenharmony_ci script = script_helper.make_script(path, "test", "1 / 0") 3817db96d56Sopenharmony_ci bc = importlib.util.cache_from_source(script) 3827db96d56Sopenharmony_ci stripdir = os.path.join(self.directory, *fullpath[:2]) 3837db96d56Sopenharmony_ci prependdir = "/foo" 3847db96d56Sopenharmony_ci compileall.compile_dir(path, quiet=True, 3857db96d56Sopenharmony_ci stripdir=stripdir, prependdir=prependdir) 3867db96d56Sopenharmony_ci rc, out, err = script_helper.assert_python_failure(bc) 3877db96d56Sopenharmony_ci expected_in = os.path.join(prependdir, *fullpath[2:]) 3887db96d56Sopenharmony_ci self.assertIn( 3897db96d56Sopenharmony_ci expected_in, 3907db96d56Sopenharmony_ci str(err, encoding=sys.getdefaultencoding()) 3917db96d56Sopenharmony_ci ) 3927db96d56Sopenharmony_ci self.assertNotIn( 3937db96d56Sopenharmony_ci stripdir, 3947db96d56Sopenharmony_ci str(err, encoding=sys.getdefaultencoding()) 3957db96d56Sopenharmony_ci ) 3967db96d56Sopenharmony_ci 3977db96d56Sopenharmony_ci def test_strip_prepend_and_ddir(self): 3987db96d56Sopenharmony_ci fullpath = ["test", "build", "real", "path", "ddir"] 3997db96d56Sopenharmony_ci path = os.path.join(self.directory, *fullpath) 4007db96d56Sopenharmony_ci os.makedirs(path) 4017db96d56Sopenharmony_ci script_helper.make_script(path, "test", "1 / 0") 4027db96d56Sopenharmony_ci with self.assertRaises(ValueError): 4037db96d56Sopenharmony_ci compileall.compile_dir(path, quiet=True, ddir="/bar", 4047db96d56Sopenharmony_ci stripdir="/foo", prependdir="/bar") 4057db96d56Sopenharmony_ci 4067db96d56Sopenharmony_ci def test_multiple_optimization_levels(self): 4077db96d56Sopenharmony_ci script = script_helper.make_script(self.directory, 4087db96d56Sopenharmony_ci "test_optimization", 4097db96d56Sopenharmony_ci "a = 0") 4107db96d56Sopenharmony_ci bc = [] 4117db96d56Sopenharmony_ci for opt_level in "", 1, 2, 3: 4127db96d56Sopenharmony_ci bc.append(importlib.util.cache_from_source(script, 4137db96d56Sopenharmony_ci optimization=opt_level)) 4147db96d56Sopenharmony_ci test_combinations = [[0, 1], [1, 2], [0, 2], [0, 1, 2]] 4157db96d56Sopenharmony_ci for opt_combination in test_combinations: 4167db96d56Sopenharmony_ci compileall.compile_file(script, quiet=True, 4177db96d56Sopenharmony_ci optimize=opt_combination) 4187db96d56Sopenharmony_ci for opt_level in opt_combination: 4197db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(bc[opt_level])) 4207db96d56Sopenharmony_ci try: 4217db96d56Sopenharmony_ci os.unlink(bc[opt_level]) 4227db96d56Sopenharmony_ci except Exception: 4237db96d56Sopenharmony_ci pass 4247db96d56Sopenharmony_ci 4257db96d56Sopenharmony_ci @os_helper.skip_unless_symlink 4267db96d56Sopenharmony_ci def test_ignore_symlink_destination(self): 4277db96d56Sopenharmony_ci # Create folders for allowed files, symlinks and prohibited area 4287db96d56Sopenharmony_ci allowed_path = os.path.join(self.directory, "test", "dir", "allowed") 4297db96d56Sopenharmony_ci symlinks_path = os.path.join(self.directory, "test", "dir", "symlinks") 4307db96d56Sopenharmony_ci prohibited_path = os.path.join(self.directory, "test", "dir", "prohibited") 4317db96d56Sopenharmony_ci os.makedirs(allowed_path) 4327db96d56Sopenharmony_ci os.makedirs(symlinks_path) 4337db96d56Sopenharmony_ci os.makedirs(prohibited_path) 4347db96d56Sopenharmony_ci 4357db96d56Sopenharmony_ci # Create scripts and symlinks and remember their byte-compiled versions 4367db96d56Sopenharmony_ci allowed_script = script_helper.make_script(allowed_path, "test_allowed", "a = 0") 4377db96d56Sopenharmony_ci prohibited_script = script_helper.make_script(prohibited_path, "test_prohibited", "a = 0") 4387db96d56Sopenharmony_ci allowed_symlink = os.path.join(symlinks_path, "test_allowed.py") 4397db96d56Sopenharmony_ci prohibited_symlink = os.path.join(symlinks_path, "test_prohibited.py") 4407db96d56Sopenharmony_ci os.symlink(allowed_script, allowed_symlink) 4417db96d56Sopenharmony_ci os.symlink(prohibited_script, prohibited_symlink) 4427db96d56Sopenharmony_ci allowed_bc = importlib.util.cache_from_source(allowed_symlink) 4437db96d56Sopenharmony_ci prohibited_bc = importlib.util.cache_from_source(prohibited_symlink) 4447db96d56Sopenharmony_ci 4457db96d56Sopenharmony_ci compileall.compile_dir(symlinks_path, quiet=True, limit_sl_dest=allowed_path) 4467db96d56Sopenharmony_ci 4477db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(allowed_bc)) 4487db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(prohibited_bc)) 4497db96d56Sopenharmony_ci 4507db96d56Sopenharmony_ci 4517db96d56Sopenharmony_ciclass CompileallTestsWithSourceEpoch(CompileallTestsBase, 4527db96d56Sopenharmony_ci unittest.TestCase, 4537db96d56Sopenharmony_ci metaclass=SourceDateEpochTestMeta, 4547db96d56Sopenharmony_ci source_date_epoch=True): 4557db96d56Sopenharmony_ci pass 4567db96d56Sopenharmony_ci 4577db96d56Sopenharmony_ci 4587db96d56Sopenharmony_ciclass CompileallTestsWithoutSourceEpoch(CompileallTestsBase, 4597db96d56Sopenharmony_ci unittest.TestCase, 4607db96d56Sopenharmony_ci metaclass=SourceDateEpochTestMeta, 4617db96d56Sopenharmony_ci source_date_epoch=False): 4627db96d56Sopenharmony_ci pass 4637db96d56Sopenharmony_ci 4647db96d56Sopenharmony_ci 4657db96d56Sopenharmony_ci# WASI does not have a temp directory and uses cwd instead. The cwd contains 4667db96d56Sopenharmony_ci# non-ASCII chars, so _walk_dir() fails to encode self.directory. 4677db96d56Sopenharmony_ci@unittest.skipIf(support.is_wasi, "tempdir is not encodable on WASI") 4687db96d56Sopenharmony_ciclass EncodingTest(unittest.TestCase): 4697db96d56Sopenharmony_ci """Issue 6716: compileall should escape source code when printing errors 4707db96d56Sopenharmony_ci to stdout.""" 4717db96d56Sopenharmony_ci 4727db96d56Sopenharmony_ci def setUp(self): 4737db96d56Sopenharmony_ci self.directory = tempfile.mkdtemp() 4747db96d56Sopenharmony_ci self.source_path = os.path.join(self.directory, '_test.py') 4757db96d56Sopenharmony_ci with open(self.source_path, 'w', encoding='utf-8') as file: 4767db96d56Sopenharmony_ci file.write('# -*- coding: utf-8 -*-\n') 4777db96d56Sopenharmony_ci file.write('print u"\u20ac"\n') 4787db96d56Sopenharmony_ci 4797db96d56Sopenharmony_ci def tearDown(self): 4807db96d56Sopenharmony_ci shutil.rmtree(self.directory) 4817db96d56Sopenharmony_ci 4827db96d56Sopenharmony_ci def test_error(self): 4837db96d56Sopenharmony_ci try: 4847db96d56Sopenharmony_ci orig_stdout = sys.stdout 4857db96d56Sopenharmony_ci sys.stdout = io.TextIOWrapper(io.BytesIO(),encoding='ascii') 4867db96d56Sopenharmony_ci compileall.compile_dir(self.directory) 4877db96d56Sopenharmony_ci finally: 4887db96d56Sopenharmony_ci sys.stdout = orig_stdout 4897db96d56Sopenharmony_ci 4907db96d56Sopenharmony_ci 4917db96d56Sopenharmony_ciclass CommandLineTestsBase: 4927db96d56Sopenharmony_ci """Test compileall's CLI.""" 4937db96d56Sopenharmony_ci 4947db96d56Sopenharmony_ci def setUp(self): 4957db96d56Sopenharmony_ci self.directory = tempfile.mkdtemp() 4967db96d56Sopenharmony_ci self.addCleanup(os_helper.rmtree, self.directory) 4977db96d56Sopenharmony_ci self.pkgdir = os.path.join(self.directory, 'foo') 4987db96d56Sopenharmony_ci os.mkdir(self.pkgdir) 4997db96d56Sopenharmony_ci self.pkgdir_cachedir = os.path.join(self.pkgdir, '__pycache__') 5007db96d56Sopenharmony_ci # Create the __init__.py and a package module. 5017db96d56Sopenharmony_ci self.initfn = script_helper.make_script(self.pkgdir, '__init__', '') 5027db96d56Sopenharmony_ci self.barfn = script_helper.make_script(self.pkgdir, 'bar', '') 5037db96d56Sopenharmony_ci 5047db96d56Sopenharmony_ci @contextlib.contextmanager 5057db96d56Sopenharmony_ci def temporary_pycache_prefix(self): 5067db96d56Sopenharmony_ci """Adjust and restore sys.pycache_prefix.""" 5077db96d56Sopenharmony_ci old_prefix = sys.pycache_prefix 5087db96d56Sopenharmony_ci new_prefix = os.path.join(self.directory, '__testcache__') 5097db96d56Sopenharmony_ci try: 5107db96d56Sopenharmony_ci sys.pycache_prefix = new_prefix 5117db96d56Sopenharmony_ci yield { 5127db96d56Sopenharmony_ci 'PYTHONPATH': self.directory, 5137db96d56Sopenharmony_ci 'PYTHONPYCACHEPREFIX': new_prefix, 5147db96d56Sopenharmony_ci } 5157db96d56Sopenharmony_ci finally: 5167db96d56Sopenharmony_ci sys.pycache_prefix = old_prefix 5177db96d56Sopenharmony_ci 5187db96d56Sopenharmony_ci def _get_run_args(self, args): 5197db96d56Sopenharmony_ci return [*support.optim_args_from_interpreter_flags(), 5207db96d56Sopenharmony_ci '-S', '-m', 'compileall', 5217db96d56Sopenharmony_ci *args] 5227db96d56Sopenharmony_ci 5237db96d56Sopenharmony_ci def assertRunOK(self, *args, **env_vars): 5247db96d56Sopenharmony_ci rc, out, err = script_helper.assert_python_ok( 5257db96d56Sopenharmony_ci *self._get_run_args(args), **env_vars, 5267db96d56Sopenharmony_ci PYTHONIOENCODING='utf-8') 5277db96d56Sopenharmony_ci self.assertEqual(b'', err) 5287db96d56Sopenharmony_ci return out 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ci def assertRunNotOK(self, *args, **env_vars): 5317db96d56Sopenharmony_ci rc, out, err = script_helper.assert_python_failure( 5327db96d56Sopenharmony_ci *self._get_run_args(args), **env_vars, 5337db96d56Sopenharmony_ci PYTHONIOENCODING='utf-8') 5347db96d56Sopenharmony_ci return rc, out, err 5357db96d56Sopenharmony_ci 5367db96d56Sopenharmony_ci def assertCompiled(self, fn): 5377db96d56Sopenharmony_ci path = importlib.util.cache_from_source(fn) 5387db96d56Sopenharmony_ci self.assertTrue(os.path.exists(path)) 5397db96d56Sopenharmony_ci 5407db96d56Sopenharmony_ci def assertNotCompiled(self, fn): 5417db96d56Sopenharmony_ci path = importlib.util.cache_from_source(fn) 5427db96d56Sopenharmony_ci self.assertFalse(os.path.exists(path)) 5437db96d56Sopenharmony_ci 5447db96d56Sopenharmony_ci def test_no_args_compiles_path(self): 5457db96d56Sopenharmony_ci # Note that -l is implied for the no args case. 5467db96d56Sopenharmony_ci bazfn = script_helper.make_script(self.directory, 'baz', '') 5477db96d56Sopenharmony_ci with self.temporary_pycache_prefix() as env: 5487db96d56Sopenharmony_ci self.assertRunOK(**env) 5497db96d56Sopenharmony_ci self.assertCompiled(bazfn) 5507db96d56Sopenharmony_ci self.assertNotCompiled(self.initfn) 5517db96d56Sopenharmony_ci self.assertNotCompiled(self.barfn) 5527db96d56Sopenharmony_ci 5537db96d56Sopenharmony_ci @without_source_date_epoch # timestamp invalidation test 5547db96d56Sopenharmony_ci def test_no_args_respects_force_flag(self): 5557db96d56Sopenharmony_ci bazfn = script_helper.make_script(self.directory, 'baz', '') 5567db96d56Sopenharmony_ci with self.temporary_pycache_prefix() as env: 5577db96d56Sopenharmony_ci self.assertRunOK(**env) 5587db96d56Sopenharmony_ci pycpath = importlib.util.cache_from_source(bazfn) 5597db96d56Sopenharmony_ci # Set atime/mtime backward to avoid file timestamp resolution issues 5607db96d56Sopenharmony_ci os.utime(pycpath, (time.time()-60,)*2) 5617db96d56Sopenharmony_ci mtime = os.stat(pycpath).st_mtime 5627db96d56Sopenharmony_ci # Without force, no recompilation 5637db96d56Sopenharmony_ci self.assertRunOK(**env) 5647db96d56Sopenharmony_ci mtime2 = os.stat(pycpath).st_mtime 5657db96d56Sopenharmony_ci self.assertEqual(mtime, mtime2) 5667db96d56Sopenharmony_ci # Now force it. 5677db96d56Sopenharmony_ci self.assertRunOK('-f', **env) 5687db96d56Sopenharmony_ci mtime2 = os.stat(pycpath).st_mtime 5697db96d56Sopenharmony_ci self.assertNotEqual(mtime, mtime2) 5707db96d56Sopenharmony_ci 5717db96d56Sopenharmony_ci def test_no_args_respects_quiet_flag(self): 5727db96d56Sopenharmony_ci script_helper.make_script(self.directory, 'baz', '') 5737db96d56Sopenharmony_ci with self.temporary_pycache_prefix() as env: 5747db96d56Sopenharmony_ci noisy = self.assertRunOK(**env) 5757db96d56Sopenharmony_ci self.assertIn(b'Listing ', noisy) 5767db96d56Sopenharmony_ci quiet = self.assertRunOK('-q', **env) 5777db96d56Sopenharmony_ci self.assertNotIn(b'Listing ', quiet) 5787db96d56Sopenharmony_ci 5797db96d56Sopenharmony_ci # Ensure that the default behavior of compileall's CLI is to create 5807db96d56Sopenharmony_ci # PEP 3147/PEP 488 pyc files. 5817db96d56Sopenharmony_ci for name, ext, switch in [ 5827db96d56Sopenharmony_ci ('normal', 'pyc', []), 5837db96d56Sopenharmony_ci ('optimize', 'opt-1.pyc', ['-O']), 5847db96d56Sopenharmony_ci ('doubleoptimize', 'opt-2.pyc', ['-OO']), 5857db96d56Sopenharmony_ci ]: 5867db96d56Sopenharmony_ci def f(self, ext=ext, switch=switch): 5877db96d56Sopenharmony_ci script_helper.assert_python_ok(*(switch + 5887db96d56Sopenharmony_ci ['-m', 'compileall', '-q', self.pkgdir])) 5897db96d56Sopenharmony_ci # Verify the __pycache__ directory contents. 5907db96d56Sopenharmony_ci self.assertTrue(os.path.exists(self.pkgdir_cachedir)) 5917db96d56Sopenharmony_ci expected = sorted(base.format(sys.implementation.cache_tag, ext) 5927db96d56Sopenharmony_ci for base in ('__init__.{}.{}', 'bar.{}.{}')) 5937db96d56Sopenharmony_ci self.assertEqual(sorted(os.listdir(self.pkgdir_cachedir)), expected) 5947db96d56Sopenharmony_ci # Make sure there are no .pyc files in the source directory. 5957db96d56Sopenharmony_ci self.assertFalse([fn for fn in os.listdir(self.pkgdir) 5967db96d56Sopenharmony_ci if fn.endswith(ext)]) 5977db96d56Sopenharmony_ci locals()['test_pep3147_paths_' + name] = f 5987db96d56Sopenharmony_ci 5997db96d56Sopenharmony_ci def test_legacy_paths(self): 6007db96d56Sopenharmony_ci # Ensure that with the proper switch, compileall leaves legacy 6017db96d56Sopenharmony_ci # pyc files, and no __pycache__ directory. 6027db96d56Sopenharmony_ci self.assertRunOK('-b', '-q', self.pkgdir) 6037db96d56Sopenharmony_ci # Verify the __pycache__ directory contents. 6047db96d56Sopenharmony_ci self.assertFalse(os.path.exists(self.pkgdir_cachedir)) 6057db96d56Sopenharmony_ci expected = sorted(['__init__.py', '__init__.pyc', 'bar.py', 6067db96d56Sopenharmony_ci 'bar.pyc']) 6077db96d56Sopenharmony_ci self.assertEqual(sorted(os.listdir(self.pkgdir)), expected) 6087db96d56Sopenharmony_ci 6097db96d56Sopenharmony_ci def test_multiple_runs(self): 6107db96d56Sopenharmony_ci # Bug 8527 reported that multiple calls produced empty 6117db96d56Sopenharmony_ci # __pycache__/__pycache__ directories. 6127db96d56Sopenharmony_ci self.assertRunOK('-q', self.pkgdir) 6137db96d56Sopenharmony_ci # Verify the __pycache__ directory contents. 6147db96d56Sopenharmony_ci self.assertTrue(os.path.exists(self.pkgdir_cachedir)) 6157db96d56Sopenharmony_ci cachecachedir = os.path.join(self.pkgdir_cachedir, '__pycache__') 6167db96d56Sopenharmony_ci self.assertFalse(os.path.exists(cachecachedir)) 6177db96d56Sopenharmony_ci # Call compileall again. 6187db96d56Sopenharmony_ci self.assertRunOK('-q', self.pkgdir) 6197db96d56Sopenharmony_ci self.assertTrue(os.path.exists(self.pkgdir_cachedir)) 6207db96d56Sopenharmony_ci self.assertFalse(os.path.exists(cachecachedir)) 6217db96d56Sopenharmony_ci 6227db96d56Sopenharmony_ci @without_source_date_epoch # timestamp invalidation test 6237db96d56Sopenharmony_ci def test_force(self): 6247db96d56Sopenharmony_ci self.assertRunOK('-q', self.pkgdir) 6257db96d56Sopenharmony_ci pycpath = importlib.util.cache_from_source(self.barfn) 6267db96d56Sopenharmony_ci # set atime/mtime backward to avoid file timestamp resolution issues 6277db96d56Sopenharmony_ci os.utime(pycpath, (time.time()-60,)*2) 6287db96d56Sopenharmony_ci mtime = os.stat(pycpath).st_mtime 6297db96d56Sopenharmony_ci # without force, no recompilation 6307db96d56Sopenharmony_ci self.assertRunOK('-q', self.pkgdir) 6317db96d56Sopenharmony_ci mtime2 = os.stat(pycpath).st_mtime 6327db96d56Sopenharmony_ci self.assertEqual(mtime, mtime2) 6337db96d56Sopenharmony_ci # now force it. 6347db96d56Sopenharmony_ci self.assertRunOK('-q', '-f', self.pkgdir) 6357db96d56Sopenharmony_ci mtime2 = os.stat(pycpath).st_mtime 6367db96d56Sopenharmony_ci self.assertNotEqual(mtime, mtime2) 6377db96d56Sopenharmony_ci 6387db96d56Sopenharmony_ci def test_recursion_control(self): 6397db96d56Sopenharmony_ci subpackage = os.path.join(self.pkgdir, 'spam') 6407db96d56Sopenharmony_ci os.mkdir(subpackage) 6417db96d56Sopenharmony_ci subinitfn = script_helper.make_script(subpackage, '__init__', '') 6427db96d56Sopenharmony_ci hamfn = script_helper.make_script(subpackage, 'ham', '') 6437db96d56Sopenharmony_ci self.assertRunOK('-q', '-l', self.pkgdir) 6447db96d56Sopenharmony_ci self.assertNotCompiled(subinitfn) 6457db96d56Sopenharmony_ci self.assertFalse(os.path.exists(os.path.join(subpackage, '__pycache__'))) 6467db96d56Sopenharmony_ci self.assertRunOK('-q', self.pkgdir) 6477db96d56Sopenharmony_ci self.assertCompiled(subinitfn) 6487db96d56Sopenharmony_ci self.assertCompiled(hamfn) 6497db96d56Sopenharmony_ci 6507db96d56Sopenharmony_ci def test_recursion_limit(self): 6517db96d56Sopenharmony_ci subpackage = os.path.join(self.pkgdir, 'spam') 6527db96d56Sopenharmony_ci subpackage2 = os.path.join(subpackage, 'ham') 6537db96d56Sopenharmony_ci subpackage3 = os.path.join(subpackage2, 'eggs') 6547db96d56Sopenharmony_ci for pkg in (subpackage, subpackage2, subpackage3): 6557db96d56Sopenharmony_ci script_helper.make_pkg(pkg) 6567db96d56Sopenharmony_ci 6577db96d56Sopenharmony_ci subinitfn = os.path.join(subpackage, '__init__.py') 6587db96d56Sopenharmony_ci hamfn = script_helper.make_script(subpackage, 'ham', '') 6597db96d56Sopenharmony_ci spamfn = script_helper.make_script(subpackage2, 'spam', '') 6607db96d56Sopenharmony_ci eggfn = script_helper.make_script(subpackage3, 'egg', '') 6617db96d56Sopenharmony_ci 6627db96d56Sopenharmony_ci self.assertRunOK('-q', '-r 0', self.pkgdir) 6637db96d56Sopenharmony_ci self.assertNotCompiled(subinitfn) 6647db96d56Sopenharmony_ci self.assertFalse( 6657db96d56Sopenharmony_ci os.path.exists(os.path.join(subpackage, '__pycache__'))) 6667db96d56Sopenharmony_ci 6677db96d56Sopenharmony_ci self.assertRunOK('-q', '-r 1', self.pkgdir) 6687db96d56Sopenharmony_ci self.assertCompiled(subinitfn) 6697db96d56Sopenharmony_ci self.assertCompiled(hamfn) 6707db96d56Sopenharmony_ci self.assertNotCompiled(spamfn) 6717db96d56Sopenharmony_ci 6727db96d56Sopenharmony_ci self.assertRunOK('-q', '-r 2', self.pkgdir) 6737db96d56Sopenharmony_ci self.assertCompiled(subinitfn) 6747db96d56Sopenharmony_ci self.assertCompiled(hamfn) 6757db96d56Sopenharmony_ci self.assertCompiled(spamfn) 6767db96d56Sopenharmony_ci self.assertNotCompiled(eggfn) 6777db96d56Sopenharmony_ci 6787db96d56Sopenharmony_ci self.assertRunOK('-q', '-r 5', self.pkgdir) 6797db96d56Sopenharmony_ci self.assertCompiled(subinitfn) 6807db96d56Sopenharmony_ci self.assertCompiled(hamfn) 6817db96d56Sopenharmony_ci self.assertCompiled(spamfn) 6827db96d56Sopenharmony_ci self.assertCompiled(eggfn) 6837db96d56Sopenharmony_ci 6847db96d56Sopenharmony_ci @os_helper.skip_unless_symlink 6857db96d56Sopenharmony_ci def test_symlink_loop(self): 6867db96d56Sopenharmony_ci # Currently, compileall ignores symlinks to directories. 6877db96d56Sopenharmony_ci # If that limitation is ever lifted, it should protect against 6887db96d56Sopenharmony_ci # recursion in symlink loops. 6897db96d56Sopenharmony_ci pkg = os.path.join(self.pkgdir, 'spam') 6907db96d56Sopenharmony_ci script_helper.make_pkg(pkg) 6917db96d56Sopenharmony_ci os.symlink('.', os.path.join(pkg, 'evil')) 6927db96d56Sopenharmony_ci os.symlink('.', os.path.join(pkg, 'evil2')) 6937db96d56Sopenharmony_ci self.assertRunOK('-q', self.pkgdir) 6947db96d56Sopenharmony_ci self.assertCompiled(os.path.join( 6957db96d56Sopenharmony_ci self.pkgdir, 'spam', 'evil', 'evil2', '__init__.py' 6967db96d56Sopenharmony_ci )) 6977db96d56Sopenharmony_ci 6987db96d56Sopenharmony_ci def test_quiet(self): 6997db96d56Sopenharmony_ci noisy = self.assertRunOK(self.pkgdir) 7007db96d56Sopenharmony_ci quiet = self.assertRunOK('-q', self.pkgdir) 7017db96d56Sopenharmony_ci self.assertNotEqual(b'', noisy) 7027db96d56Sopenharmony_ci self.assertEqual(b'', quiet) 7037db96d56Sopenharmony_ci 7047db96d56Sopenharmony_ci def test_silent(self): 7057db96d56Sopenharmony_ci script_helper.make_script(self.pkgdir, 'crunchyfrog', 'bad(syntax') 7067db96d56Sopenharmony_ci _, quiet, _ = self.assertRunNotOK('-q', self.pkgdir) 7077db96d56Sopenharmony_ci _, silent, _ = self.assertRunNotOK('-qq', self.pkgdir) 7087db96d56Sopenharmony_ci self.assertNotEqual(b'', quiet) 7097db96d56Sopenharmony_ci self.assertEqual(b'', silent) 7107db96d56Sopenharmony_ci 7117db96d56Sopenharmony_ci def test_regexp(self): 7127db96d56Sopenharmony_ci self.assertRunOK('-q', '-x', r'ba[^\\/]*$', self.pkgdir) 7137db96d56Sopenharmony_ci self.assertNotCompiled(self.barfn) 7147db96d56Sopenharmony_ci self.assertCompiled(self.initfn) 7157db96d56Sopenharmony_ci 7167db96d56Sopenharmony_ci def test_multiple_dirs(self): 7177db96d56Sopenharmony_ci pkgdir2 = os.path.join(self.directory, 'foo2') 7187db96d56Sopenharmony_ci os.mkdir(pkgdir2) 7197db96d56Sopenharmony_ci init2fn = script_helper.make_script(pkgdir2, '__init__', '') 7207db96d56Sopenharmony_ci bar2fn = script_helper.make_script(pkgdir2, 'bar2', '') 7217db96d56Sopenharmony_ci self.assertRunOK('-q', self.pkgdir, pkgdir2) 7227db96d56Sopenharmony_ci self.assertCompiled(self.initfn) 7237db96d56Sopenharmony_ci self.assertCompiled(self.barfn) 7247db96d56Sopenharmony_ci self.assertCompiled(init2fn) 7257db96d56Sopenharmony_ci self.assertCompiled(bar2fn) 7267db96d56Sopenharmony_ci 7277db96d56Sopenharmony_ci def test_d_compile_error(self): 7287db96d56Sopenharmony_ci script_helper.make_script(self.pkgdir, 'crunchyfrog', 'bad(syntax') 7297db96d56Sopenharmony_ci rc, out, err = self.assertRunNotOK('-q', '-d', 'dinsdale', self.pkgdir) 7307db96d56Sopenharmony_ci self.assertRegex(out, b'File "dinsdale') 7317db96d56Sopenharmony_ci 7327db96d56Sopenharmony_ci def test_d_runtime_error(self): 7337db96d56Sopenharmony_ci bazfn = script_helper.make_script(self.pkgdir, 'baz', 'raise Exception') 7347db96d56Sopenharmony_ci self.assertRunOK('-q', '-d', 'dinsdale', self.pkgdir) 7357db96d56Sopenharmony_ci fn = script_helper.make_script(self.pkgdir, 'bing', 'import baz') 7367db96d56Sopenharmony_ci pyc = importlib.util.cache_from_source(bazfn) 7377db96d56Sopenharmony_ci os.rename(pyc, os.path.join(self.pkgdir, 'baz.pyc')) 7387db96d56Sopenharmony_ci os.remove(bazfn) 7397db96d56Sopenharmony_ci rc, out, err = script_helper.assert_python_failure(fn, __isolated=False) 7407db96d56Sopenharmony_ci self.assertRegex(err, b'File "dinsdale') 7417db96d56Sopenharmony_ci 7427db96d56Sopenharmony_ci def test_include_bad_file(self): 7437db96d56Sopenharmony_ci rc, out, err = self.assertRunNotOK( 7447db96d56Sopenharmony_ci '-i', os.path.join(self.directory, 'nosuchfile'), self.pkgdir) 7457db96d56Sopenharmony_ci self.assertRegex(out, b'rror.*nosuchfile') 7467db96d56Sopenharmony_ci self.assertNotRegex(err, b'Traceback') 7477db96d56Sopenharmony_ci self.assertFalse(os.path.exists(importlib.util.cache_from_source( 7487db96d56Sopenharmony_ci self.pkgdir_cachedir))) 7497db96d56Sopenharmony_ci 7507db96d56Sopenharmony_ci def test_include_file_with_arg(self): 7517db96d56Sopenharmony_ci f1 = script_helper.make_script(self.pkgdir, 'f1', '') 7527db96d56Sopenharmony_ci f2 = script_helper.make_script(self.pkgdir, 'f2', '') 7537db96d56Sopenharmony_ci f3 = script_helper.make_script(self.pkgdir, 'f3', '') 7547db96d56Sopenharmony_ci f4 = script_helper.make_script(self.pkgdir, 'f4', '') 7557db96d56Sopenharmony_ci with open(os.path.join(self.directory, 'l1'), 'w', encoding="utf-8") as l1: 7567db96d56Sopenharmony_ci l1.write(os.path.join(self.pkgdir, 'f1.py')+os.linesep) 7577db96d56Sopenharmony_ci l1.write(os.path.join(self.pkgdir, 'f2.py')+os.linesep) 7587db96d56Sopenharmony_ci self.assertRunOK('-i', os.path.join(self.directory, 'l1'), f4) 7597db96d56Sopenharmony_ci self.assertCompiled(f1) 7607db96d56Sopenharmony_ci self.assertCompiled(f2) 7617db96d56Sopenharmony_ci self.assertNotCompiled(f3) 7627db96d56Sopenharmony_ci self.assertCompiled(f4) 7637db96d56Sopenharmony_ci 7647db96d56Sopenharmony_ci def test_include_file_no_arg(self): 7657db96d56Sopenharmony_ci f1 = script_helper.make_script(self.pkgdir, 'f1', '') 7667db96d56Sopenharmony_ci f2 = script_helper.make_script(self.pkgdir, 'f2', '') 7677db96d56Sopenharmony_ci f3 = script_helper.make_script(self.pkgdir, 'f3', '') 7687db96d56Sopenharmony_ci f4 = script_helper.make_script(self.pkgdir, 'f4', '') 7697db96d56Sopenharmony_ci with open(os.path.join(self.directory, 'l1'), 'w', encoding="utf-8") as l1: 7707db96d56Sopenharmony_ci l1.write(os.path.join(self.pkgdir, 'f2.py')+os.linesep) 7717db96d56Sopenharmony_ci self.assertRunOK('-i', os.path.join(self.directory, 'l1')) 7727db96d56Sopenharmony_ci self.assertNotCompiled(f1) 7737db96d56Sopenharmony_ci self.assertCompiled(f2) 7747db96d56Sopenharmony_ci self.assertNotCompiled(f3) 7757db96d56Sopenharmony_ci self.assertNotCompiled(f4) 7767db96d56Sopenharmony_ci 7777db96d56Sopenharmony_ci def test_include_on_stdin(self): 7787db96d56Sopenharmony_ci f1 = script_helper.make_script(self.pkgdir, 'f1', '') 7797db96d56Sopenharmony_ci f2 = script_helper.make_script(self.pkgdir, 'f2', '') 7807db96d56Sopenharmony_ci f3 = script_helper.make_script(self.pkgdir, 'f3', '') 7817db96d56Sopenharmony_ci f4 = script_helper.make_script(self.pkgdir, 'f4', '') 7827db96d56Sopenharmony_ci p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-'])) 7837db96d56Sopenharmony_ci p.stdin.write((f3+os.linesep).encode('ascii')) 7847db96d56Sopenharmony_ci script_helper.kill_python(p) 7857db96d56Sopenharmony_ci self.assertNotCompiled(f1) 7867db96d56Sopenharmony_ci self.assertNotCompiled(f2) 7877db96d56Sopenharmony_ci self.assertCompiled(f3) 7887db96d56Sopenharmony_ci self.assertNotCompiled(f4) 7897db96d56Sopenharmony_ci 7907db96d56Sopenharmony_ci def test_compiles_as_much_as_possible(self): 7917db96d56Sopenharmony_ci bingfn = script_helper.make_script(self.pkgdir, 'bing', 'syntax(error') 7927db96d56Sopenharmony_ci rc, out, err = self.assertRunNotOK('nosuchfile', self.initfn, 7937db96d56Sopenharmony_ci bingfn, self.barfn) 7947db96d56Sopenharmony_ci self.assertRegex(out, b'rror') 7957db96d56Sopenharmony_ci self.assertNotCompiled(bingfn) 7967db96d56Sopenharmony_ci self.assertCompiled(self.initfn) 7977db96d56Sopenharmony_ci self.assertCompiled(self.barfn) 7987db96d56Sopenharmony_ci 7997db96d56Sopenharmony_ci def test_invalid_arg_produces_message(self): 8007db96d56Sopenharmony_ci out = self.assertRunOK('badfilename') 8017db96d56Sopenharmony_ci self.assertRegex(out, b"Can't list 'badfilename'") 8027db96d56Sopenharmony_ci 8037db96d56Sopenharmony_ci def test_pyc_invalidation_mode(self): 8047db96d56Sopenharmony_ci script_helper.make_script(self.pkgdir, 'f1', '') 8057db96d56Sopenharmony_ci pyc = importlib.util.cache_from_source( 8067db96d56Sopenharmony_ci os.path.join(self.pkgdir, 'f1.py')) 8077db96d56Sopenharmony_ci self.assertRunOK('--invalidation-mode=checked-hash', self.pkgdir) 8087db96d56Sopenharmony_ci with open(pyc, 'rb') as fp: 8097db96d56Sopenharmony_ci data = fp.read() 8107db96d56Sopenharmony_ci self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b11) 8117db96d56Sopenharmony_ci self.assertRunOK('--invalidation-mode=unchecked-hash', self.pkgdir) 8127db96d56Sopenharmony_ci with open(pyc, 'rb') as fp: 8137db96d56Sopenharmony_ci data = fp.read() 8147db96d56Sopenharmony_ci self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b01) 8157db96d56Sopenharmony_ci 8167db96d56Sopenharmony_ci @skipUnless(_have_multiprocessing, "requires multiprocessing") 8177db96d56Sopenharmony_ci def test_workers(self): 8187db96d56Sopenharmony_ci bar2fn = script_helper.make_script(self.directory, 'bar2', '') 8197db96d56Sopenharmony_ci files = [] 8207db96d56Sopenharmony_ci for suffix in range(5): 8217db96d56Sopenharmony_ci pkgdir = os.path.join(self.directory, 'foo{}'.format(suffix)) 8227db96d56Sopenharmony_ci os.mkdir(pkgdir) 8237db96d56Sopenharmony_ci fn = script_helper.make_script(pkgdir, '__init__', '') 8247db96d56Sopenharmony_ci files.append(script_helper.make_script(pkgdir, 'bar2', '')) 8257db96d56Sopenharmony_ci 8267db96d56Sopenharmony_ci self.assertRunOK(self.directory, '-j', '0') 8277db96d56Sopenharmony_ci self.assertCompiled(bar2fn) 8287db96d56Sopenharmony_ci for file in files: 8297db96d56Sopenharmony_ci self.assertCompiled(file) 8307db96d56Sopenharmony_ci 8317db96d56Sopenharmony_ci @mock.patch('compileall.compile_dir') 8327db96d56Sopenharmony_ci def test_workers_available_cores(self, compile_dir): 8337db96d56Sopenharmony_ci with mock.patch("sys.argv", 8347db96d56Sopenharmony_ci new=[sys.executable, self.directory, "-j0"]): 8357db96d56Sopenharmony_ci compileall.main() 8367db96d56Sopenharmony_ci self.assertTrue(compile_dir.called) 8377db96d56Sopenharmony_ci self.assertEqual(compile_dir.call_args[-1]['workers'], 0) 8387db96d56Sopenharmony_ci 8397db96d56Sopenharmony_ci def test_strip_and_prepend(self): 8407db96d56Sopenharmony_ci fullpath = ["test", "build", "real", "path"] 8417db96d56Sopenharmony_ci path = os.path.join(self.directory, *fullpath) 8427db96d56Sopenharmony_ci os.makedirs(path) 8437db96d56Sopenharmony_ci script = script_helper.make_script(path, "test", "1 / 0") 8447db96d56Sopenharmony_ci bc = importlib.util.cache_from_source(script) 8457db96d56Sopenharmony_ci stripdir = os.path.join(self.directory, *fullpath[:2]) 8467db96d56Sopenharmony_ci prependdir = "/foo" 8477db96d56Sopenharmony_ci self.assertRunOK("-s", stripdir, "-p", prependdir, path) 8487db96d56Sopenharmony_ci rc, out, err = script_helper.assert_python_failure(bc) 8497db96d56Sopenharmony_ci expected_in = os.path.join(prependdir, *fullpath[2:]) 8507db96d56Sopenharmony_ci self.assertIn( 8517db96d56Sopenharmony_ci expected_in, 8527db96d56Sopenharmony_ci str(err, encoding=sys.getdefaultencoding()) 8537db96d56Sopenharmony_ci ) 8547db96d56Sopenharmony_ci self.assertNotIn( 8557db96d56Sopenharmony_ci stripdir, 8567db96d56Sopenharmony_ci str(err, encoding=sys.getdefaultencoding()) 8577db96d56Sopenharmony_ci ) 8587db96d56Sopenharmony_ci 8597db96d56Sopenharmony_ci def test_multiple_optimization_levels(self): 8607db96d56Sopenharmony_ci path = os.path.join(self.directory, "optimizations") 8617db96d56Sopenharmony_ci os.makedirs(path) 8627db96d56Sopenharmony_ci script = script_helper.make_script(path, 8637db96d56Sopenharmony_ci "test_optimization", 8647db96d56Sopenharmony_ci "a = 0") 8657db96d56Sopenharmony_ci bc = [] 8667db96d56Sopenharmony_ci for opt_level in "", 1, 2, 3: 8677db96d56Sopenharmony_ci bc.append(importlib.util.cache_from_source(script, 8687db96d56Sopenharmony_ci optimization=opt_level)) 8697db96d56Sopenharmony_ci test_combinations = [["0", "1"], 8707db96d56Sopenharmony_ci ["1", "2"], 8717db96d56Sopenharmony_ci ["0", "2"], 8727db96d56Sopenharmony_ci ["0", "1", "2"]] 8737db96d56Sopenharmony_ci for opt_combination in test_combinations: 8747db96d56Sopenharmony_ci self.assertRunOK(path, *("-o" + str(n) for n in opt_combination)) 8757db96d56Sopenharmony_ci for opt_level in opt_combination: 8767db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(bc[int(opt_level)])) 8777db96d56Sopenharmony_ci try: 8787db96d56Sopenharmony_ci os.unlink(bc[opt_level]) 8797db96d56Sopenharmony_ci except Exception: 8807db96d56Sopenharmony_ci pass 8817db96d56Sopenharmony_ci 8827db96d56Sopenharmony_ci @os_helper.skip_unless_symlink 8837db96d56Sopenharmony_ci def test_ignore_symlink_destination(self): 8847db96d56Sopenharmony_ci # Create folders for allowed files, symlinks and prohibited area 8857db96d56Sopenharmony_ci allowed_path = os.path.join(self.directory, "test", "dir", "allowed") 8867db96d56Sopenharmony_ci symlinks_path = os.path.join(self.directory, "test", "dir", "symlinks") 8877db96d56Sopenharmony_ci prohibited_path = os.path.join(self.directory, "test", "dir", "prohibited") 8887db96d56Sopenharmony_ci os.makedirs(allowed_path) 8897db96d56Sopenharmony_ci os.makedirs(symlinks_path) 8907db96d56Sopenharmony_ci os.makedirs(prohibited_path) 8917db96d56Sopenharmony_ci 8927db96d56Sopenharmony_ci # Create scripts and symlinks and remember their byte-compiled versions 8937db96d56Sopenharmony_ci allowed_script = script_helper.make_script(allowed_path, "test_allowed", "a = 0") 8947db96d56Sopenharmony_ci prohibited_script = script_helper.make_script(prohibited_path, "test_prohibited", "a = 0") 8957db96d56Sopenharmony_ci allowed_symlink = os.path.join(symlinks_path, "test_allowed.py") 8967db96d56Sopenharmony_ci prohibited_symlink = os.path.join(symlinks_path, "test_prohibited.py") 8977db96d56Sopenharmony_ci os.symlink(allowed_script, allowed_symlink) 8987db96d56Sopenharmony_ci os.symlink(prohibited_script, prohibited_symlink) 8997db96d56Sopenharmony_ci allowed_bc = importlib.util.cache_from_source(allowed_symlink) 9007db96d56Sopenharmony_ci prohibited_bc = importlib.util.cache_from_source(prohibited_symlink) 9017db96d56Sopenharmony_ci 9027db96d56Sopenharmony_ci self.assertRunOK(symlinks_path, "-e", allowed_path) 9037db96d56Sopenharmony_ci 9047db96d56Sopenharmony_ci self.assertTrue(os.path.isfile(allowed_bc)) 9057db96d56Sopenharmony_ci self.assertFalse(os.path.isfile(prohibited_bc)) 9067db96d56Sopenharmony_ci 9077db96d56Sopenharmony_ci def test_hardlink_bad_args(self): 9087db96d56Sopenharmony_ci # Bad arguments combination, hardlink deduplication make sense 9097db96d56Sopenharmony_ci # only for more than one optimization level 9107db96d56Sopenharmony_ci self.assertRunNotOK(self.directory, "-o 1", "--hardlink-dupes") 9117db96d56Sopenharmony_ci 9127db96d56Sopenharmony_ci def test_hardlink(self): 9137db96d56Sopenharmony_ci # 'a = 0' code produces the same bytecode for the 3 optimization 9147db96d56Sopenharmony_ci # levels. All three .pyc files must have the same inode (hardlinks). 9157db96d56Sopenharmony_ci # 9167db96d56Sopenharmony_ci # If deduplication is disabled, all pyc files must have different 9177db96d56Sopenharmony_ci # inodes. 9187db96d56Sopenharmony_ci for dedup in (True, False): 9197db96d56Sopenharmony_ci with tempfile.TemporaryDirectory() as path: 9207db96d56Sopenharmony_ci with self.subTest(dedup=dedup): 9217db96d56Sopenharmony_ci script = script_helper.make_script(path, "script", "a = 0") 9227db96d56Sopenharmony_ci pycs = get_pycs(script) 9237db96d56Sopenharmony_ci 9247db96d56Sopenharmony_ci args = ["-q", "-o 0", "-o 1", "-o 2"] 9257db96d56Sopenharmony_ci if dedup: 9267db96d56Sopenharmony_ci args.append("--hardlink-dupes") 9277db96d56Sopenharmony_ci self.assertRunOK(path, *args) 9287db96d56Sopenharmony_ci 9297db96d56Sopenharmony_ci self.assertEqual(is_hardlink(pycs[0], pycs[1]), dedup) 9307db96d56Sopenharmony_ci self.assertEqual(is_hardlink(pycs[1], pycs[2]), dedup) 9317db96d56Sopenharmony_ci self.assertEqual(is_hardlink(pycs[0], pycs[2]), dedup) 9327db96d56Sopenharmony_ci 9337db96d56Sopenharmony_ci 9347db96d56Sopenharmony_ciclass CommandLineTestsWithSourceEpoch(CommandLineTestsBase, 9357db96d56Sopenharmony_ci unittest.TestCase, 9367db96d56Sopenharmony_ci metaclass=SourceDateEpochTestMeta, 9377db96d56Sopenharmony_ci source_date_epoch=True): 9387db96d56Sopenharmony_ci pass 9397db96d56Sopenharmony_ci 9407db96d56Sopenharmony_ci 9417db96d56Sopenharmony_ciclass CommandLineTestsNoSourceEpoch(CommandLineTestsBase, 9427db96d56Sopenharmony_ci unittest.TestCase, 9437db96d56Sopenharmony_ci metaclass=SourceDateEpochTestMeta, 9447db96d56Sopenharmony_ci source_date_epoch=False): 9457db96d56Sopenharmony_ci pass 9467db96d56Sopenharmony_ci 9477db96d56Sopenharmony_ci 9487db96d56Sopenharmony_ci 9497db96d56Sopenharmony_ci@unittest.skipUnless(hasattr(os, 'link'), 'requires os.link') 9507db96d56Sopenharmony_ciclass HardlinkDedupTestsBase: 9517db96d56Sopenharmony_ci # Test hardlink_dupes parameter of compileall.compile_dir() 9527db96d56Sopenharmony_ci 9537db96d56Sopenharmony_ci def setUp(self): 9547db96d56Sopenharmony_ci self.path = None 9557db96d56Sopenharmony_ci 9567db96d56Sopenharmony_ci @contextlib.contextmanager 9577db96d56Sopenharmony_ci def temporary_directory(self): 9587db96d56Sopenharmony_ci with tempfile.TemporaryDirectory() as path: 9597db96d56Sopenharmony_ci self.path = path 9607db96d56Sopenharmony_ci yield path 9617db96d56Sopenharmony_ci self.path = None 9627db96d56Sopenharmony_ci 9637db96d56Sopenharmony_ci def make_script(self, code, name="script"): 9647db96d56Sopenharmony_ci return script_helper.make_script(self.path, name, code) 9657db96d56Sopenharmony_ci 9667db96d56Sopenharmony_ci def compile_dir(self, *, dedup=True, optimize=(0, 1, 2), force=False): 9677db96d56Sopenharmony_ci compileall.compile_dir(self.path, quiet=True, optimize=optimize, 9687db96d56Sopenharmony_ci hardlink_dupes=dedup, force=force) 9697db96d56Sopenharmony_ci 9707db96d56Sopenharmony_ci def test_bad_args(self): 9717db96d56Sopenharmony_ci # Bad arguments combination, hardlink deduplication make sense 9727db96d56Sopenharmony_ci # only for more than one optimization level 9737db96d56Sopenharmony_ci with self.temporary_directory(): 9747db96d56Sopenharmony_ci self.make_script("pass") 9757db96d56Sopenharmony_ci with self.assertRaises(ValueError): 9767db96d56Sopenharmony_ci compileall.compile_dir(self.path, quiet=True, optimize=0, 9777db96d56Sopenharmony_ci hardlink_dupes=True) 9787db96d56Sopenharmony_ci with self.assertRaises(ValueError): 9797db96d56Sopenharmony_ci # same optimization level specified twice: 9807db96d56Sopenharmony_ci # compile_dir() removes duplicates 9817db96d56Sopenharmony_ci compileall.compile_dir(self.path, quiet=True, optimize=[0, 0], 9827db96d56Sopenharmony_ci hardlink_dupes=True) 9837db96d56Sopenharmony_ci 9847db96d56Sopenharmony_ci def create_code(self, docstring=False, assertion=False): 9857db96d56Sopenharmony_ci lines = [] 9867db96d56Sopenharmony_ci if docstring: 9877db96d56Sopenharmony_ci lines.append("'module docstring'") 9887db96d56Sopenharmony_ci lines.append('x = 1') 9897db96d56Sopenharmony_ci if assertion: 9907db96d56Sopenharmony_ci lines.append("assert x == 1") 9917db96d56Sopenharmony_ci return '\n'.join(lines) 9927db96d56Sopenharmony_ci 9937db96d56Sopenharmony_ci def iter_codes(self): 9947db96d56Sopenharmony_ci for docstring in (False, True): 9957db96d56Sopenharmony_ci for assertion in (False, True): 9967db96d56Sopenharmony_ci code = self.create_code(docstring=docstring, assertion=assertion) 9977db96d56Sopenharmony_ci yield (code, docstring, assertion) 9987db96d56Sopenharmony_ci 9997db96d56Sopenharmony_ci def test_disabled(self): 10007db96d56Sopenharmony_ci # Deduplication disabled, no hardlinks 10017db96d56Sopenharmony_ci for code, docstring, assertion in self.iter_codes(): 10027db96d56Sopenharmony_ci with self.subTest(docstring=docstring, assertion=assertion): 10037db96d56Sopenharmony_ci with self.temporary_directory(): 10047db96d56Sopenharmony_ci script = self.make_script(code) 10057db96d56Sopenharmony_ci pycs = get_pycs(script) 10067db96d56Sopenharmony_ci self.compile_dir(dedup=False) 10077db96d56Sopenharmony_ci self.assertFalse(is_hardlink(pycs[0], pycs[1])) 10087db96d56Sopenharmony_ci self.assertFalse(is_hardlink(pycs[0], pycs[2])) 10097db96d56Sopenharmony_ci self.assertFalse(is_hardlink(pycs[1], pycs[2])) 10107db96d56Sopenharmony_ci 10117db96d56Sopenharmony_ci def check_hardlinks(self, script, docstring=False, assertion=False): 10127db96d56Sopenharmony_ci pycs = get_pycs(script) 10137db96d56Sopenharmony_ci self.assertEqual(is_hardlink(pycs[0], pycs[1]), 10147db96d56Sopenharmony_ci not assertion) 10157db96d56Sopenharmony_ci self.assertEqual(is_hardlink(pycs[0], pycs[2]), 10167db96d56Sopenharmony_ci not assertion and not docstring) 10177db96d56Sopenharmony_ci self.assertEqual(is_hardlink(pycs[1], pycs[2]), 10187db96d56Sopenharmony_ci not docstring) 10197db96d56Sopenharmony_ci 10207db96d56Sopenharmony_ci def test_hardlink(self): 10217db96d56Sopenharmony_ci # Test deduplication on all combinations 10227db96d56Sopenharmony_ci for code, docstring, assertion in self.iter_codes(): 10237db96d56Sopenharmony_ci with self.subTest(docstring=docstring, assertion=assertion): 10247db96d56Sopenharmony_ci with self.temporary_directory(): 10257db96d56Sopenharmony_ci script = self.make_script(code) 10267db96d56Sopenharmony_ci self.compile_dir() 10277db96d56Sopenharmony_ci self.check_hardlinks(script, docstring, assertion) 10287db96d56Sopenharmony_ci 10297db96d56Sopenharmony_ci def test_only_two_levels(self): 10307db96d56Sopenharmony_ci # Don't build the 3 optimization levels, but only 2 10317db96d56Sopenharmony_ci for opts in ((0, 1), (1, 2), (0, 2)): 10327db96d56Sopenharmony_ci with self.subTest(opts=opts): 10337db96d56Sopenharmony_ci with self.temporary_directory(): 10347db96d56Sopenharmony_ci # code with no dostring and no assertion: 10357db96d56Sopenharmony_ci # same bytecode for all optimization levels 10367db96d56Sopenharmony_ci script = self.make_script(self.create_code()) 10377db96d56Sopenharmony_ci self.compile_dir(optimize=opts) 10387db96d56Sopenharmony_ci pyc1 = get_pyc(script, opts[0]) 10397db96d56Sopenharmony_ci pyc2 = get_pyc(script, opts[1]) 10407db96d56Sopenharmony_ci self.assertTrue(is_hardlink(pyc1, pyc2)) 10417db96d56Sopenharmony_ci 10427db96d56Sopenharmony_ci def test_duplicated_levels(self): 10437db96d56Sopenharmony_ci # compile_dir() must not fail if optimize contains duplicated 10447db96d56Sopenharmony_ci # optimization levels and/or if optimization levels are not sorted. 10457db96d56Sopenharmony_ci with self.temporary_directory(): 10467db96d56Sopenharmony_ci # code with no dostring and no assertion: 10477db96d56Sopenharmony_ci # same bytecode for all optimization levels 10487db96d56Sopenharmony_ci script = self.make_script(self.create_code()) 10497db96d56Sopenharmony_ci self.compile_dir(optimize=[1, 0, 1, 0]) 10507db96d56Sopenharmony_ci pyc1 = get_pyc(script, 0) 10517db96d56Sopenharmony_ci pyc2 = get_pyc(script, 1) 10527db96d56Sopenharmony_ci self.assertTrue(is_hardlink(pyc1, pyc2)) 10537db96d56Sopenharmony_ci 10547db96d56Sopenharmony_ci def test_recompilation(self): 10557db96d56Sopenharmony_ci # Test compile_dir() when pyc files already exists and the script 10567db96d56Sopenharmony_ci # content changed 10577db96d56Sopenharmony_ci with self.temporary_directory(): 10587db96d56Sopenharmony_ci script = self.make_script("a = 0") 10597db96d56Sopenharmony_ci self.compile_dir() 10607db96d56Sopenharmony_ci # All three levels have the same inode 10617db96d56Sopenharmony_ci self.check_hardlinks(script) 10627db96d56Sopenharmony_ci 10637db96d56Sopenharmony_ci pycs = get_pycs(script) 10647db96d56Sopenharmony_ci inode = os.stat(pycs[0]).st_ino 10657db96d56Sopenharmony_ci 10667db96d56Sopenharmony_ci # Change of the module content 10677db96d56Sopenharmony_ci script = self.make_script("print(0)") 10687db96d56Sopenharmony_ci 10697db96d56Sopenharmony_ci # Recompilation without -o 1 10707db96d56Sopenharmony_ci self.compile_dir(optimize=[0, 2], force=True) 10717db96d56Sopenharmony_ci 10727db96d56Sopenharmony_ci # opt-1.pyc should have the same inode as before and others should not 10737db96d56Sopenharmony_ci self.assertEqual(inode, os.stat(pycs[1]).st_ino) 10747db96d56Sopenharmony_ci self.assertTrue(is_hardlink(pycs[0], pycs[2])) 10757db96d56Sopenharmony_ci self.assertNotEqual(inode, os.stat(pycs[2]).st_ino) 10767db96d56Sopenharmony_ci # opt-1.pyc and opt-2.pyc have different content 10777db96d56Sopenharmony_ci self.assertFalse(filecmp.cmp(pycs[1], pycs[2], shallow=True)) 10787db96d56Sopenharmony_ci 10797db96d56Sopenharmony_ci def test_import(self): 10807db96d56Sopenharmony_ci # Test that import updates a single pyc file when pyc files already 10817db96d56Sopenharmony_ci # exists and the script content changed 10827db96d56Sopenharmony_ci with self.temporary_directory(): 10837db96d56Sopenharmony_ci script = self.make_script(self.create_code(), name="module") 10847db96d56Sopenharmony_ci self.compile_dir() 10857db96d56Sopenharmony_ci # All three levels have the same inode 10867db96d56Sopenharmony_ci self.check_hardlinks(script) 10877db96d56Sopenharmony_ci 10887db96d56Sopenharmony_ci pycs = get_pycs(script) 10897db96d56Sopenharmony_ci inode = os.stat(pycs[0]).st_ino 10907db96d56Sopenharmony_ci 10917db96d56Sopenharmony_ci # Change of the module content 10927db96d56Sopenharmony_ci script = self.make_script("print(0)", name="module") 10937db96d56Sopenharmony_ci 10947db96d56Sopenharmony_ci # Import the module in Python with -O (optimization level 1) 10957db96d56Sopenharmony_ci script_helper.assert_python_ok( 10967db96d56Sopenharmony_ci "-O", "-c", "import module", __isolated=False, PYTHONPATH=self.path 10977db96d56Sopenharmony_ci ) 10987db96d56Sopenharmony_ci 10997db96d56Sopenharmony_ci # Only opt-1.pyc is changed 11007db96d56Sopenharmony_ci self.assertEqual(inode, os.stat(pycs[0]).st_ino) 11017db96d56Sopenharmony_ci self.assertEqual(inode, os.stat(pycs[2]).st_ino) 11027db96d56Sopenharmony_ci self.assertFalse(is_hardlink(pycs[1], pycs[2])) 11037db96d56Sopenharmony_ci # opt-1.pyc and opt-2.pyc have different content 11047db96d56Sopenharmony_ci self.assertFalse(filecmp.cmp(pycs[1], pycs[2], shallow=True)) 11057db96d56Sopenharmony_ci 11067db96d56Sopenharmony_ci 11077db96d56Sopenharmony_ciclass HardlinkDedupTestsWithSourceEpoch(HardlinkDedupTestsBase, 11087db96d56Sopenharmony_ci unittest.TestCase, 11097db96d56Sopenharmony_ci metaclass=SourceDateEpochTestMeta, 11107db96d56Sopenharmony_ci source_date_epoch=True): 11117db96d56Sopenharmony_ci pass 11127db96d56Sopenharmony_ci 11137db96d56Sopenharmony_ci 11147db96d56Sopenharmony_ciclass HardlinkDedupTestsNoSourceEpoch(HardlinkDedupTestsBase, 11157db96d56Sopenharmony_ci unittest.TestCase, 11167db96d56Sopenharmony_ci metaclass=SourceDateEpochTestMeta, 11177db96d56Sopenharmony_ci source_date_epoch=False): 11187db96d56Sopenharmony_ci pass 11197db96d56Sopenharmony_ci 11207db96d56Sopenharmony_ci 11217db96d56Sopenharmony_ciif __name__ == "__main__": 11227db96d56Sopenharmony_ci unittest.main() 1123