diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index af885c51224064..205457f3b4801e 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -11,6 +11,7 @@ import time import unittest import io +import errno from unittest import mock, skipUnless try: @@ -41,20 +42,57 @@ def setUp(self): os.mkdir(self.subdirectory) self.source_path3 = os.path.join(self.subdirectory, '_test3.py') shutil.copyfile(self.source_path, self.source_path3) - many_directories = [str(number) for number in range(1, 100)] - self.long_path = os.path.join(self.directory, - "long", - *many_directories) - os.makedirs(self.long_path) - self.source_path_long = os.path.join(self.long_path, '_test4.py') - shutil.copyfile(self.source_path, self.source_path_long) - self.bc_path_long = importlib.util.cache_from_source( - self.source_path_long - ) def tearDown(self): shutil.rmtree(self.directory) + def create_long_path(self): + long_path = os.path.join(self.directory, "long") + + # Create a long path, 10 directories at a time. + # It will be 100 directories deep, or shorter if the OS limits it. + for i in range(10): + longer_path = os.path.join( + long_path, *(f"long_directory_{i}_{j}" for j in range(10)) + ) + + # Check if we can open __pycache__/*.pyc. + # Also, put in the source file that we want to compile + longer_source = os.path.join(longer_path, '_test_long.py') + longer_cache = importlib.util.cache_from_source(longer_source) + try: + os.makedirs(longer_path) + shutil.copyfile(self.source_path, longer_source) + os.makedirs(os.path.dirname(longer_cache)) + # Make sure we can write to the cache + with open(longer_cache, 'w'): + pass + except FileNotFoundError: + # On Windows, a FileNotFoundError("The filename or extension + # is too long") is raised for long paths + if sys.platform == "win32": + break + else: + raise + except OSError as exc: + if exc.errno == errno.ENAMETOOLONG: + break + else: + raise + + # Remove the __pycache__ + shutil.rmtree(os.path.dirname(longer_cache)) + + long_path = longer_path + long_source = longer_source + long_cache = longer_cache + + if i < 2: + raise ValueError('Path limit is too short') + + self.source_path_long = long_source + self.bc_path_long = long_cache + def add_bad_source_file(self): self.bad_source_path = os.path.join(self.directory, '_test_bad.py') with open(self.bad_source_path, 'w') as file: @@ -204,13 +242,14 @@ def test_compile_missing_multiprocessing(self, compile_file_mock): compileall.compile_dir(self.directory, quiet=True, workers=5) self.assertTrue(compile_file_mock.called) - def text_compile_dir_maxlevels(self): + def test_compile_dir_maxlevels(self): # Test the actual impact of maxlevels attr + self.create_long_path() compileall.compile_dir(os.path.join(self.directory, "long"), maxlevels=10, quiet=True) self.assertFalse(os.path.isfile(self.bc_path_long)) compileall.compile_dir(os.path.join(self.directory, "long"), - maxlevels=110, quiet=True) + quiet=True) self.assertTrue(os.path.isfile(self.bc_path_long)) def test_strip_only(self):