]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-105931: Fix surprising compileall stripdir behaviour (GH-108671)
authorhetmankp <728670+hetmankp@users.noreply.github.com>
Mon, 23 Oct 2023 13:55:39 +0000 (00:55 +1100)
committerGitHub <noreply@github.com>
Mon, 23 Oct 2023 13:55:39 +0000 (13:55 +0000)
Before, the '-s STRIPDIR' option on
compileall lead to some surprising results as it only strips away
path components that match, but leaves alone the non-matching ones
interspersed in between.

For example, with: python -m compileall -s/path/to/another/src
/path/to/build/src/file.py

The resulting written path will be: build/file.py

This fix only strips directories that are a fully matching prefix of the
source path. If a stripdir is provided that is not a valid prefix, a
warning will be displayed (which can be silenced with '-qq').

Lib/compileall.py
Lib/test/test_compileall.py
Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst [new file with mode: 0644]

index d394156cedc4e717da0392f8f109b0f5214d27c4..9b53086bf41380ea6d1e0f3348c77fc29f3dbdac 100644 (file)
@@ -172,13 +172,13 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
     if stripdir is not None:
         fullname_parts = fullname.split(os.path.sep)
         stripdir_parts = stripdir.split(os.path.sep)
-        ddir_parts = list(fullname_parts)
 
-        for spart, opart in zip(stripdir_parts, fullname_parts):
-            if spart == opart:
-                ddir_parts.remove(spart)
-
-        dfile = os.path.join(*ddir_parts)
+        if stripdir_parts != fullname_parts[:len(stripdir_parts)]:
+            if quiet < 2:
+                print("The stripdir path {!r} is not a valid prefix for "
+                      "source path {!r}; ignoring".format(stripdir, fullname))
+        else:
+            dfile = os.path.join(*fullname_parts[len(stripdir_parts):])
 
     if prependdir is not None:
         if dfile is None:
index 9cd92ad365c5a90f1a569927bfa717917c5b23ca..83a9532aecfac80027867a14d738f54aea752070 100644 (file)
@@ -362,6 +362,29 @@ class CompileallTestsBase:
             str(err, encoding=sys.getdefaultencoding())
         )
 
+    def test_strip_only_invalid(self):
+        fullpath = ["test", "build", "real", "path"]
+        path = os.path.join(self.directory, *fullpath)
+        os.makedirs(path)
+        script = script_helper.make_script(path, "test", "1 / 0")
+        bc = importlib.util.cache_from_source(script)
+        stripdir = os.path.join(self.directory, *(fullpath[:2] + ['fake']))
+        compileall.compile_dir(path, quiet=True, stripdir=stripdir)
+        rc, out, err = script_helper.assert_python_failure(bc)
+        expected_not_in = os.path.join(self.directory, *fullpath[2:])
+        self.assertIn(
+            path,
+            str(err, encoding=sys.getdefaultencoding())
+        )
+        self.assertNotIn(
+            expected_not_in,
+            str(err, encoding=sys.getdefaultencoding())
+        )
+        self.assertNotIn(
+            stripdir,
+            str(err, encoding=sys.getdefaultencoding())
+        )
+
     def test_prepend_only(self):
         fullpath = ["test", "build", "real", "path"]
         path = os.path.join(self.directory, *fullpath)
diff --git a/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst b/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst
new file mode 100644 (file)
index 0000000..4e769ec
--- /dev/null
@@ -0,0 +1,8 @@
+Change :mod:`compileall` to only strip the stripdir prefix from the full path
+recorded in the compiled ``.pyc`` file, when the prefix matches the start of
+the full path in its entirety. When the prefix does not match, no stripping is
+performed and a warning to this effect is displayed.
+
+Previously all path components of the stripdir prefix that matched the full
+path were removed, while those that did not match were left alone (including
+ones interspersed between matching components).