[Python-checkins] bpo-38112: compileall: Skip long path path on Windows if the path can't be created (GH-16414)

Petr Viktorin webhook-mailer at python.org
Thu Sep 26 05:54:06 EDT 2019


https://github.com/python/cpython/commit/4267c989e7fc6cd528e8a1d04a07fac5cca85ec7
commit: 4267c989e7fc6cd528e8a1d04a07fac5cca85ec7
branch: master
author: Petr Viktorin <encukou at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-09-26T11:53:51+02:00
summary:

bpo-38112: compileall: Skip long path path on Windows if the path can't be created (GH-16414)

This avoids the buildbot failure on Windows:
```
FileNotFoundError: [WinError 206] The filename or extension is too long: 'd:\\temp\\tmp5r3z438t\\long\\1\\2\\3\\4\\5\\6\\7\\8\\9\\10\\11\\12\\13\\14\\15\\16\\17\\18\\19\\20\\21\\22\\23\\24\\25\\26\\27\\28\\29\\30\\31\\32\\33\\34\\35\\36\\37\\38\\39\\40\\41\\42\\43\\44\\45\\46\\47\\48\\49\\50\\51\\52\\53\\54\\55\\56\\57\\58\\59\\60\\61\\62\\63\\64\\65\\66\\67\\68\\69\\70\\71\\72\\73\\74\\75\\76\\77\\78'
```
Creates a path that's long but avoids OS restrictions.

https://bugs.python.org/issue38112

files:
M Lib/test/test_compileall.py

diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py
index af885c512240..205457f3b480 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):



More information about the Python-checkins mailing list