Skip to content

Commit 1d1d1b0

Browse files
committed
gh-144278: Enable overriding TAG in sysmodule.c to change or disable sys.implementation.cache_tag
1 parent 89e6607 commit 1d1d1b0

29 files changed

+294
-183
lines changed

Lib/compileall.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
165165
stripdir = os.fspath(stripdir) if stripdir is not None else None
166166
name = os.path.basename(fullname)
167167

168+
# Without a cache_tag, we can only create legacy .pyc files. None of our
169+
# callers seem to expect this, so the best we can do is fail without raising
170+
if not legacy and sys.implementation.cache_tag is None:
171+
if not quiet:
172+
print("No cache tag is available to generate .pyc path for",
173+
repr(fullname))
174+
return False
175+
168176
dfile = None
169177

170178
if ddir is not None:

Lib/ensurepip/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
177177
args += ["--user"]
178178
if verbosity:
179179
args += ["-" + "v" * verbosity]
180+
if sys.implementation.cache_tag is None:
181+
args += ["--no-compile"]
180182

181183
return _run_pip([*args, "pip"], [os.fsdecode(tmp_wheel_path)])
182184

Lib/py_compile.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,10 @@ def main():
194194
else:
195195
filenames = args.filenames
196196
for filename in filenames:
197+
cfilename = (None if sys.implementation.cache_tag
198+
else f"{filename.rpartition('.')[0]}.pyc")
197199
try:
198-
compile(filename, doraise=True)
200+
compile(filename, cfilename, doraise=True)
199201
except PyCompileError as error:
200202
if args.quiet:
201203
parser.exit(1)

Lib/test/support/import_helper.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import importlib.machinery
55
import importlib.util
66
import os
7+
import py_compile
78
import shutil
89
import sys
910
import textwrap
@@ -49,20 +50,31 @@ def forget(modname):
4950
# combinations of PEP 3147/488 and legacy pyc files.
5051
unlink(source + 'c')
5152
for opt in ('', 1, 2):
52-
unlink(importlib.util.cache_from_source(source, optimization=opt))
53+
try:
54+
unlink(importlib.util.cache_from_source(source, optimization=opt))
55+
except NotImplementedError:
56+
pass
5357

5458

55-
def make_legacy_pyc(source):
59+
def make_legacy_pyc(source, allow_compile=False):
5660
"""Move a PEP 3147/488 pyc file to its legacy pyc location.
5761
5862
:param source: The file system path to the source file. The source file
59-
does not need to exist, however the PEP 3147/488 pyc file must exist.
63+
does not need to exist, however the PEP 3147/488 pyc file must exist or
64+
allow_compile must be set.
65+
:param allow_compile: If True, uses py_compile to create a .pyc if it does
66+
not exist. This should be passed as True if cache_tag may be None.
6067
:return: The file system path to the legacy pyc file.
6168
"""
62-
pyc_file = importlib.util.cache_from_source(source)
6369
assert source.endswith('.py')
6470
legacy_pyc = source + 'c'
65-
shutil.move(pyc_file, legacy_pyc)
71+
try:
72+
pyc_file = importlib.util.cache_from_source(source)
73+
shutil.move(pyc_file, legacy_pyc)
74+
except (FileNotFoundError, NotImplementedError):
75+
if not allow_compile:
76+
raise
77+
py_compile.compile(source, legacy_pyc, doraise=True)
6678
return legacy_pyc
6779

6880

Lib/test/test_argparse.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import io
88
import operator
99
import os
10-
import py_compile
1110
import shutil
1211
import stat
1312
import sys
@@ -7162,9 +7161,8 @@ def make_script(self, dirname, basename, *, compiled=False):
71627161
script_name = script_helper.make_script(dirname, basename, self.source)
71637162
if not compiled:
71647163
return script_name
7165-
py_compile.compile(script_name, doraise=True)
7164+
pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True)
71667165
os.remove(script_name)
7167-
pyc_file = import_helper.make_legacy_pyc(script_name)
71687166
return pyc_file
71697167

71707168
def make_zip_script(self, script_name, name_in_zip=None):

Lib/test/test_capi/test_import.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,10 @@ def check_executecode_pathnames(self, execute_code_func, object=False):
289289
self.check_executecodemodule(execute_code_func, NULL, pathname)
290290

291291
# Test NULL pathname and non-NULL cpathname
292-
pyc_filename = importlib.util.cache_from_source(__file__)
292+
try:
293+
pyc_filename = importlib.util.cache_from_source(__file__)
294+
except NotImplementedError:
295+
return
293296
py_filename = importlib.util.source_from_cache(pyc_filename)
294297
origin = self.check_executecodemodule(execute_code_func, NULL, pyc_filename)
295298
if not object:

Lib/test/test_cmd_line_script.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,8 @@ def test_script_abspath(self):
240240
def test_script_compiled(self):
241241
with os_helper.temp_dir() as script_dir:
242242
script_name = _make_test_script(script_dir, 'script')
243-
py_compile.compile(script_name, doraise=True)
243+
pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True)
244244
os.remove(script_name)
245-
pyc_file = import_helper.make_legacy_pyc(script_name)
246245
self._check_script(pyc_file, pyc_file,
247246
pyc_file, script_dir, None,
248247
importlib.machinery.SourcelessFileLoader)
@@ -257,9 +256,8 @@ def test_directory(self):
257256
def test_directory_compiled(self):
258257
with os_helper.temp_dir() as script_dir:
259258
script_name = _make_test_script(script_dir, '__main__')
260-
py_compile.compile(script_name, doraise=True)
259+
pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True)
261260
os.remove(script_name)
262-
pyc_file = import_helper.make_legacy_pyc(script_name)
263261
self._check_script(script_dir, pyc_file, script_dir,
264262
script_dir, '',
265263
importlib.machinery.SourcelessFileLoader)
@@ -279,8 +277,8 @@ def test_zipfile(self):
279277
def test_zipfile_compiled_timestamp(self):
280278
with os_helper.temp_dir() as script_dir:
281279
script_name = _make_test_script(script_dir, '__main__')
282-
compiled_name = py_compile.compile(
283-
script_name, doraise=True,
280+
compiled_name = script_name + 'c'
281+
py_compile.compile(script_name, compiled_name, doraise=True,
284282
invalidation_mode=py_compile.PycInvalidationMode.TIMESTAMP)
285283
zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name)
286284
self._check_script(zip_name, run_name, zip_name, zip_name, '',
@@ -289,8 +287,8 @@ def test_zipfile_compiled_timestamp(self):
289287
def test_zipfile_compiled_checked_hash(self):
290288
with os_helper.temp_dir() as script_dir:
291289
script_name = _make_test_script(script_dir, '__main__')
292-
compiled_name = py_compile.compile(
293-
script_name, doraise=True,
290+
compiled_name = script_name + 'c'
291+
py_compile.compile(script_name, compiled_name, doraise=True,
294292
invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH)
295293
zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name)
296294
self._check_script(zip_name, run_name, zip_name, zip_name, '',
@@ -299,8 +297,8 @@ def test_zipfile_compiled_checked_hash(self):
299297
def test_zipfile_compiled_unchecked_hash(self):
300298
with os_helper.temp_dir() as script_dir:
301299
script_name = _make_test_script(script_dir, '__main__')
302-
compiled_name = py_compile.compile(
303-
script_name, doraise=True,
300+
compiled_name = script_name + 'c'
301+
py_compile.compile(script_name, compiled_name, doraise=True,
304302
invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH)
305303
zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name)
306304
self._check_script(zip_name, run_name, zip_name, zip_name, '',
@@ -353,9 +351,8 @@ def test_package_compiled(self):
353351
pkg_dir = os.path.join(script_dir, 'test_pkg')
354352
make_pkg(pkg_dir)
355353
script_name = _make_test_script(pkg_dir, '__main__')
356-
compiled_name = py_compile.compile(script_name, doraise=True)
354+
pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True)
357355
os.remove(script_name)
358-
pyc_file = import_helper.make_legacy_pyc(script_name)
359356
self._check_script(["-m", "test_pkg"], pyc_file,
360357
pyc_file, script_dir, 'test_pkg',
361358
importlib.machinery.SourcelessFileLoader,

Lib/test/test_compileall.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
from test.support.os_helper import FakePath
3434

3535

36+
if sys.implementation.cache_tag is None:
37+
raise unittest.SkipTest('requires sys.implementation.cache_tag is not None')
38+
39+
3640
def get_pyc(script, opt):
3741
if not opt:
3842
# Replace None and 0 with ''

Lib/test/test_ensurepip.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
import ensurepip._uninstall
1313

1414

15+
if sys.implementation.cache_tag is None:
16+
COMPILE_OPT = ["--no-compile"]
17+
else:
18+
COMPILE_OPT = []
19+
20+
1521
class TestPackages(unittest.TestCase):
1622
def touch(self, directory, filename):
1723
fullname = os.path.join(directory, filename)
@@ -85,7 +91,7 @@ def test_basic_bootstrapping(self):
8591
self.run_pip.assert_called_once_with(
8692
[
8793
"install", "--no-cache-dir", "--no-index", "--find-links",
88-
unittest.mock.ANY, "pip",
94+
unittest.mock.ANY, *COMPILE_OPT, "pip",
8995
],
9096
unittest.mock.ANY,
9197
)
@@ -99,7 +105,7 @@ def test_bootstrapping_with_root(self):
99105
self.run_pip.assert_called_once_with(
100106
[
101107
"install", "--no-cache-dir", "--no-index", "--find-links",
102-
unittest.mock.ANY, "--root", "/foo/bar/",
108+
unittest.mock.ANY, "--root", "/foo/bar/", *COMPILE_OPT,
103109
"pip",
104110
],
105111
unittest.mock.ANY,
@@ -111,7 +117,7 @@ def test_bootstrapping_with_user(self):
111117
self.run_pip.assert_called_once_with(
112118
[
113119
"install", "--no-cache-dir", "--no-index", "--find-links",
114-
unittest.mock.ANY, "--user", "pip",
120+
unittest.mock.ANY, "--user", *COMPILE_OPT, "pip",
115121
],
116122
unittest.mock.ANY,
117123
)
@@ -122,7 +128,7 @@ def test_bootstrapping_with_upgrade(self):
122128
self.run_pip.assert_called_once_with(
123129
[
124130
"install", "--no-cache-dir", "--no-index", "--find-links",
125-
unittest.mock.ANY, "--upgrade", "pip",
131+
unittest.mock.ANY, "--upgrade", *COMPILE_OPT, "pip",
126132
],
127133
unittest.mock.ANY,
128134
)
@@ -133,7 +139,7 @@ def test_bootstrapping_with_verbosity_1(self):
133139
self.run_pip.assert_called_once_with(
134140
[
135141
"install", "--no-cache-dir", "--no-index", "--find-links",
136-
unittest.mock.ANY, "-v", "pip",
142+
unittest.mock.ANY, "-v", *COMPILE_OPT, "pip",
137143
],
138144
unittest.mock.ANY,
139145
)
@@ -144,7 +150,7 @@ def test_bootstrapping_with_verbosity_2(self):
144150
self.run_pip.assert_called_once_with(
145151
[
146152
"install", "--no-cache-dir", "--no-index", "--find-links",
147-
unittest.mock.ANY, "-vv", "pip",
153+
unittest.mock.ANY, "-vv", *COMPILE_OPT, "pip",
148154
],
149155
unittest.mock.ANY,
150156
)
@@ -155,7 +161,7 @@ def test_bootstrapping_with_verbosity_3(self):
155161
self.run_pip.assert_called_once_with(
156162
[
157163
"install", "--no-cache-dir", "--no-index", "--find-links",
158-
unittest.mock.ANY, "-vvv", "pip",
164+
unittest.mock.ANY, "-vvv", *COMPILE_OPT, "pip",
159165
],
160166
unittest.mock.ANY,
161167
)
@@ -312,7 +318,7 @@ def test_basic_bootstrapping(self):
312318
self.run_pip.assert_called_once_with(
313319
[
314320
"install", "--no-cache-dir", "--no-index", "--find-links",
315-
unittest.mock.ANY, "pip",
321+
unittest.mock.ANY, *COMPILE_OPT, "pip",
316322
],
317323
unittest.mock.ANY,
318324
)

Lib/test/test_import/__init__.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676

7777

7878
skip_if_dont_write_bytecode = unittest.skipIf(
79-
sys.dont_write_bytecode,
79+
sys.dont_write_bytecode or sys.implementation.cache_tag is None,
8080
"test meaningful only when writing bytecode")
8181

8282

@@ -504,7 +504,7 @@ def test_module_with_large_stack(self, module='longlist'):
504504
try:
505505
# Compile & remove .py file; we only need .pyc.
506506
# Bytecode must be relocated from the PEP 3147 bytecode-only location.
507-
py_compile.compile(filename)
507+
make_legacy_pyc(filename, allow_compile=True)
508508
finally:
509509
unlink(filename)
510510

@@ -514,7 +514,6 @@ def test_module_with_large_stack(self, module='longlist'):
514514

515515
namespace = {}
516516
try:
517-
make_legacy_pyc(filename)
518517
# This used to crash.
519518
exec('import ' + module, None, namespace)
520519
finally:
@@ -1399,7 +1398,10 @@ def func():
13991398
"""
14001399
dir_name = os.path.abspath(TESTFN)
14011400
file_name = os.path.join(dir_name, module_name) + os.extsep + "py"
1402-
compiled_name = importlib.util.cache_from_source(file_name)
1401+
try:
1402+
compiled_name = importlib.util.cache_from_source(file_name)
1403+
except NotImplementedError:
1404+
compiled_name = None
14031405

14041406
def setUp(self):
14051407
self.sys_path = sys.path[:]
@@ -1417,7 +1419,8 @@ def tearDown(self):
14171419
else:
14181420
unload(self.module_name)
14191421
unlink(self.file_name)
1420-
unlink(self.compiled_name)
1422+
if self.compiled_name:
1423+
unlink(self.compiled_name)
14211424
rmtree(self.dir_name)
14221425

14231426
def import_module(self):
@@ -1436,6 +1439,8 @@ def test_basics(self):
14361439
self.assertEqual(mod.code_filename, self.file_name)
14371440
self.assertEqual(mod.func_filename, self.file_name)
14381441

1442+
@unittest.skipIf(sys.implementation.cache_tag is None,
1443+
'requires sys.implementation.cache_tag is not None')
14391444
def test_incorrect_code_name(self):
14401445
py_compile.compile(self.file_name, dfile="another_module.py")
14411446
mod = self.import_module()
@@ -1445,28 +1450,31 @@ def test_incorrect_code_name(self):
14451450

14461451
def test_module_without_source(self):
14471452
target = "another_module.py"
1448-
py_compile.compile(self.file_name, dfile=target)
1453+
pyc_file = self.file_name + 'c'
1454+
py_compile.compile(self.file_name, pyc_file, dfile=target)
14491455
os.remove(self.file_name)
1450-
pyc_file = make_legacy_pyc(self.file_name)
14511456
importlib.invalidate_caches()
14521457
mod = self.import_module()
14531458
self.assertEqual(mod.module_filename, pyc_file)
14541459
self.assertEqual(mod.code_filename, target)
14551460
self.assertEqual(mod.func_filename, target)
14561461

14571462
def test_foreign_code(self):
1458-
py_compile.compile(self.file_name)
1459-
with open(self.compiled_name, "rb") as f:
1463+
compiled_name = self.compiled_name or (self.file_name + 'c')
1464+
py_compile.compile(self.file_name, compiled_name)
1465+
with open(compiled_name, "rb") as f:
14601466
header = f.read(16)
14611467
code = marshal.load(f)
14621468
constants = list(code.co_consts)
14631469
foreign_code = importlib.import_module.__code__
14641470
pos = constants.index(1000)
14651471
constants[pos] = foreign_code
14661472
code = code.replace(co_consts=tuple(constants))
1467-
with open(self.compiled_name, "wb") as f:
1473+
with open(compiled_name, "wb") as f:
14681474
f.write(header)
14691475
marshal.dump(code, f)
1476+
if not self.compiled_name:
1477+
os.remove(self.file_name)
14701478
mod = self.import_module()
14711479
self.assertEqual(mod.constant.co_filename, foreign_code.co_filename)
14721480

0 commit comments

Comments
 (0)