Skip to content

Commit e8099f2

Browse files
committed
1 parent c316b78 commit e8099f2

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

compileall2.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ def compile_dir(dir, maxlevels=None, ddir=None, force=False,
113113
hardlink_dupes: hardlink duplicated pyc files
114114
"""
115115
ProcessPoolExecutor = None
116+
if ddir is not None and (stripdir is not None or prependdir is not None):
117+
raise ValueError(("Destination dir (ddir) cannot be used "
118+
"in combination with stripdir or prependdir"))
119+
if ddir is not None:
120+
stripdir = dir
121+
prependdir = ddir
122+
ddir = None
116123
if workers is not None:
117124
if workers < 0:
118125
raise ValueError('workers must be greater or equal to 0')

test_compileall2.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import compileall2 as compileall
33
import importlib.util
44
import test.test_importlib.util
5+
import marshal
56
import os
67
import pathlib
78
import py_compile
@@ -39,6 +40,19 @@ def _optim_args_from_interpreter_flags():
3940
args.append('-' + 'O' * value)
4041
return args
4142

43+
# Backported from Lib/test/test_importlib/util.py
44+
# Added in: https://github.com/python/cpython/pull/18676/files
45+
def get_code_from_pyc(pyc_path):
46+
"""Reads a pyc file and returns the unmarshalled code object within.
47+
No header validation is performed.
48+
"""
49+
with open(pyc_path, 'rb') as pyc_f:
50+
if compileall.PY37:
51+
pyc_f.seek(16)
52+
else:
53+
pyc_f.seek(12)
54+
return marshal.load(pyc_f)
55+
4256
# Use the original one for Python > 3.5 and backported function otherwise
4357
if compileall.PY36:
4458
optim_args_from_interpreter_flags = support.optim_args_from_interpreter_flags
@@ -274,6 +288,47 @@ def test_compile_dir_maxlevels(self):
274288
compileall.compile_dir(self.directory, quiet=True, maxlevels=depth)
275289
self.assertTrue(os.path.isfile(pyc_filename))
276290

291+
def _test_ddir_only(self, *, ddir, parallel=True):
292+
"""Recursive compile_dir ddir must contain package paths; bpo39769."""
293+
fullpath = ["test", "foo"]
294+
path = self.directory
295+
mods = []
296+
for subdir in fullpath:
297+
path = os.path.join(path, subdir)
298+
os.mkdir(path)
299+
script_helper.make_script(path, "__init__", "")
300+
mods.append(script_helper.make_script(path, "mod",
301+
"def fn(): 1/0\nfn()\n"))
302+
compileall.compile_dir(
303+
self.directory, quiet=True, ddir=ddir,
304+
workers=2 if parallel else 1)
305+
self.assertTrue(mods)
306+
for mod in mods:
307+
self.assertTrue(mod.startswith(self.directory), mod)
308+
modcode = importlib.util.cache_from_source(mod)
309+
modpath = mod[len(self.directory+os.sep):]
310+
_, _, err = script_helper.assert_python_failure(modcode)
311+
expected_in = os.path.join(ddir, modpath)
312+
mod_code_obj = get_code_from_pyc(modcode)
313+
self.assertEqual(mod_code_obj.co_filename, expected_in)
314+
self.assertIn('"{}"'.format(expected_in), os.fsdecode(err))
315+
316+
def test_ddir_only_one_worker(self):
317+
"""Recursive compile_dir ddir= contains package paths; bpo39769."""
318+
return self._test_ddir_only(ddir="<a prefix>", parallel=False)
319+
320+
def test_ddir_multiple_workers(self):
321+
"""Recursive compile_dir ddir= contains package paths; bpo39769."""
322+
return self._test_ddir_only(ddir="<a prefix>", parallel=True)
323+
324+
def test_ddir_empty_only_one_worker(self):
325+
"""Recursive compile_dir ddir='' contains package paths; bpo39769."""
326+
return self._test_ddir_only(ddir="", parallel=False)
327+
328+
def test_ddir_empty_multiple_workers(self):
329+
"""Recursive compile_dir ddir='' contains package paths; bpo39769."""
330+
return self._test_ddir_only(ddir="", parallel=True)
331+
277332
def test_strip_only(self):
278333
fullpath = ["test", "build", "real", "path"]
279334
path = os.path.join(self.directory, *fullpath)

0 commit comments

Comments
 (0)