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.
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 03ca79f..77c14bc 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -474,6 +474,18 @@
return source_path if _path_isfile(source_stats) else bytecode_path
+def _calc_mode(path):
+ """Calculate the mode permissions for a bytecode file."""
+ try:
+ mode = _os.stat(path).st_mode
+ except OSError:
+ mode = 0o666
+ # We always ensure write access so we can update cached files
+ # later even when the source files are read-only on Windows (#6074)
+ mode |= 0o200
+ return mode
+
+
def _verbose_message(message, *args, verbosity=1):
"""Print the message to stderr if -v/PYTHONVERBOSE is turned on."""
if sys.flags.verbose >= verbosity:
@@ -1060,13 +1072,7 @@
def _cache_bytecode(self, source_path, bytecode_path, data):
# Adapt between the two APIs
- try:
- mode = _os.stat(source_path).st_mode
- except OSError:
- mode = 0o666
- # We always ensure write access so we can update cached files
- # later even when the source files are read-only on Windows (#6074)
- mode |= 0o200
+ mode = _calc_mode(source_path)
return self.set_data(bytecode_path, data, _mode=mode)
def set_data(self, path, data, *, _mode=0o666):
diff --git a/Lib/py_compile.py b/Lib/py_compile.py
index d255a2d..701e8ac 100644
--- a/Lib/py_compile.py
+++ b/Lib/py_compile.py
@@ -106,7 +106,7 @@
source_bytes = loader.get_data(file)
try:
code = loader.source_to_code(source_bytes, dfile or file,
- _optimize=optimize)
+ _optimize=optimize)
except Exception as err:
py_exc = PyCompileError(err.__class__, err, dfile or file)
if doraise:
@@ -121,11 +121,13 @@
except FileExistsError:
pass
source_stats = loader.path_stats(file)
- bytecode = importlib._bootstrap._code_to_bytecode(code,
- source_stats['mtime'], len(source_bytes))
- loader._cache_bytecode(file, cfile, bytecode)
+ bytecode = importlib._bootstrap._code_to_bytecode(
+ code, source_stats['mtime'], source_stats['size'])
+ mode = importlib._bootstrap._calc_mode(file)
+ importlib._bootstrap._write_atomic(cfile, bytecode, mode)
return cfile
+
def main(args=None):
"""Compile several source files.
diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py
index f3c1a6a..13947b1 100644
--- a/Lib/test/test_py_compile.py
+++ b/Lib/test/test_py_compile.py
@@ -2,6 +2,7 @@
import os
import py_compile
import shutil
+import stat
import tempfile
import unittest
@@ -54,8 +55,18 @@
self.assertTrue(os.path.exists(self.pyc_path))
self.assertFalse(os.path.exists(self.cache_path))
-def test_main():
- support.run_unittest(PyCompileTests)
+ def test_exceptions_propagate(self):
+ # Make sure that exceptions raised thanks to issues with writing
+ # bytecode.
+ # http://bugs.python.org/issue17244
+ mode = os.stat(self.directory)
+ os.chmod(self.directory, stat.S_IREAD)
+ try:
+ with self.assertRaises(IOError):
+ py_compile.compile(self.source_path, self.pyc_path)
+ finally:
+ os.chmod(self.directory, mode.st_mode)
+
if __name__ == "__main__":
- test_main()
+ unittest.main()
\ No newline at end of file