Skip to content

Commit edfd6ae

Browse files
committed
Issue #17244: Don't mask exceptions raised during the creation of
bytecode files in py_compile. Thanks to Arfrever Frehtes Taifersar Arahesis for the bug report.
1 parent 672559f commit edfd6ae

File tree

4 files changed

+3512
-3481
lines changed

4 files changed

+3512
-3481
lines changed

Lib/importlib/_bootstrap.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,18 @@ def _get_sourcefile(bytecode_path):
474474
return source_path if _path_isfile(source_stats) else bytecode_path
475475

476476

477+
def _calc_mode(path):
478+
"""Calculate the mode permissions for a bytecode file."""
479+
try:
480+
mode = _os.stat(path).st_mode
481+
except OSError:
482+
mode = 0o666
483+
# We always ensure write access so we can update cached files
484+
# later even when the source files are read-only on Windows (#6074)
485+
mode |= 0o200
486+
return mode
487+
488+
477489
def _verbose_message(message, *args, verbosity=1):
478490
"""Print the message to stderr if -v/PYTHONVERBOSE is turned on."""
479491
if sys.flags.verbose >= verbosity:
@@ -1060,13 +1072,7 @@ def path_stats(self, path):
10601072

10611073
def _cache_bytecode(self, source_path, bytecode_path, data):
10621074
# Adapt between the two APIs
1063-
try:
1064-
mode = _os.stat(source_path).st_mode
1065-
except OSError:
1066-
mode = 0o666
1067-
# We always ensure write access so we can update cached files
1068-
# later even when the source files are read-only on Windows (#6074)
1069-
mode |= 0o200
1075+
mode = _calc_mode(source_path)
10701076
return self.set_data(bytecode_path, data, _mode=mode)
10711077

10721078
def set_data(self, path, data, *, _mode=0o666):

Lib/py_compile.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
106106
source_bytes = loader.get_data(file)
107107
try:
108108
code = loader.source_to_code(source_bytes, dfile or file,
109-
_optimize=optimize)
109+
_optimize=optimize)
110110
except Exception as err:
111111
py_exc = PyCompileError(err.__class__, err, dfile or file)
112112
if doraise:
@@ -121,11 +121,13 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
121121
except FileExistsError:
122122
pass
123123
source_stats = loader.path_stats(file)
124-
bytecode = importlib._bootstrap._code_to_bytecode(code,
125-
source_stats['mtime'], len(source_bytes))
126-
loader._cache_bytecode(file, cfile, bytecode)
124+
bytecode = importlib._bootstrap._code_to_bytecode(
125+
code, source_stats['mtime'], source_stats['size'])
126+
mode = importlib._bootstrap._calc_mode(file)
127+
importlib._bootstrap._write_atomic(cfile, bytecode, mode)
127128
return cfile
128129

130+
129131
def main(args=None):
130132
"""Compile several source files.
131133

Lib/test/test_py_compile.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import py_compile
44
import shutil
5+
import stat
56
import tempfile
67
import unittest
78

@@ -54,8 +55,18 @@ def test_relative_path(self):
5455
self.assertTrue(os.path.exists(self.pyc_path))
5556
self.assertFalse(os.path.exists(self.cache_path))
5657

57-
def test_main():
58-
support.run_unittest(PyCompileTests)
58+
def test_exceptions_propagate(self):
59+
# Make sure that exceptions raised thanks to issues with writing
60+
# bytecode.
61+
# http://bugs.python.org/issue17244
62+
mode = os.stat(self.directory)
63+
os.chmod(self.directory, stat.S_IREAD)
64+
try:
65+
with self.assertRaises(IOError):
66+
py_compile.compile(self.source_path, self.pyc_path)
67+
finally:
68+
os.chmod(self.directory, mode.st_mode)
69+
5970

6071
if __name__ == "__main__":
61-
test_main()
72+
unittest.main()

0 commit comments

Comments
 (0)