Skip to content

Commit e96ea20

Browse files
committed
pythongh-105931: Fix surprising compileall stripdir behaviour
This resolves issue python#105931. Currently the '-s STRIPDIR' option on compileall can 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').
1 parent 24b9bdd commit e96ea20

3 files changed

Lines changed: 36 additions & 6 deletions

File tree

‎Lib/compileall.py‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,12 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
172172
if stripdir is not None:
173173
fullname_parts = fullname.split(os.path.sep)
174174
stripdir_parts = stripdir.split(os.path.sep)
175-
ddir_parts = list(fullname_parts)
176175

177-
for spart, opart in zip(stripdir_parts, fullname_parts):
178-
if spart == opart:
179-
ddir_parts.remove(spart)
180-
181-
dfile = os.path.join(*ddir_parts)
176+
if stripdir_parts != fullname_parts[:len(stripdir_parts)]:
177+
if quiet < 2:
178+
print("The stripdir path {!r} is not a valid prefix for source path {!r}; ignoring".format(stripdir, fullname))
179+
else:
180+
dfile = os.path.join(*fullname_parts[len(stripdir_parts):])
182181

183182
if prependdir is not None:
184183
if dfile is None:

‎Lib/test/test_compileall.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,29 @@ def test_strip_only(self):
358358
str(err, encoding=sys.getdefaultencoding())
359359
)
360360

361+
def test_strip_only_invalid(self):
362+
fullpath = ["test", "build", "real", "path"]
363+
path = os.path.join(self.directory, *fullpath)
364+
os.makedirs(path)
365+
script = script_helper.make_script(path, "test", "1 / 0")
366+
bc = importlib.util.cache_from_source(script)
367+
stripdir = os.path.join(self.directory, *(fullpath[:2] + ['fake']))
368+
compileall.compile_dir(path, quiet=True, stripdir=stripdir)
369+
rc, out, err = script_helper.assert_python_failure(bc)
370+
expected_not_in = os.path.join(self.directory, *fullpath[2:])
371+
self.assertIn(
372+
path,
373+
str(err, encoding=sys.getdefaultencoding())
374+
)
375+
self.assertNotIn(
376+
expected_not_in,
377+
str(err, encoding=sys.getdefaultencoding())
378+
)
379+
self.assertNotIn(
380+
stripdir,
381+
str(err, encoding=sys.getdefaultencoding())
382+
)
383+
361384
def test_prepend_only(self):
362385
fullpath = ["test", "build", "real", "path"]
363386
path = os.path.join(self.directory, *fullpath)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Change :mod:`compileall` to only strip the stripdir prefix from the full path
2+
recorded in the compiled ``.pyc`` file, when the prefix matches the start of
3+
the full path in its entirety. When the prefix does not match, no stripping is
4+
performed and a warning to this effect is displayed.
5+
6+
Previously all path components of the stripdir prefix that matched the full
7+
path were removed, while those that did not match were left alone (including
8+
ones interspersed between matching components).

0 commit comments

Comments
 (0)