When trying to write new bytecode, importlib was not catching the IOError

thrown if the file happened to be read-only to keep the failure silent.

Fixes issue #7187. Thanks, Dave Malcolm for the report and analysis of the
problem.
This commit is contained in:
Brett Cannon 2009-11-07 23:55:05 +00:00
parent 1b184d547f
commit e52c919d67
4 changed files with 32 additions and 2 deletions

View File

@ -526,9 +526,9 @@ class _PyPycFileLoader(PyPycLoader, _PyFileLoader):
bytecode_path = self.bytecode_path(name) bytecode_path = self.bytecode_path(name)
if not bytecode_path: if not bytecode_path:
bytecode_path = self._base_path + _suffix_list(imp.PY_COMPILED)[0] bytecode_path = self._base_path + _suffix_list(imp.PY_COMPILED)[0]
file = _io.FileIO(bytecode_path, 'w') # Assuming bytes.
try: try:
with _closing(file) as bytecode_file: # Assuming bytes.
with _closing(_io.FileIO(bytecode_path, 'w')) as bytecode_file:
bytecode_file.write(data) bytecode_file.write(data)
return True return True
except IOError as exc: except IOError as exc:

View File

@ -6,6 +6,7 @@ from . import util as source_util
import imp import imp
import os import os
import py_compile import py_compile
import stat
import sys import sys
import unittest import unittest
@ -121,6 +122,10 @@ class BadBytecodeTest(unittest.TestCase):
But if the marshal data is bad, even if the magic number and timestamp But if the marshal data is bad, even if the magic number and timestamp
work, a ValueError is raised and the source is not used [bad marshal]. work, a ValueError is raised and the source is not used [bad marshal].
The case of not being able to write out the bytecode must also be handled
as it's possible it was made read-only. In that instance the attempt to
write the bytecode should fail silently [bytecode read-only].
""" """
def import_(self, file, module_name): def import_(self, file, module_name):
@ -159,6 +164,7 @@ class BadBytecodeTest(unittest.TestCase):
self.assertEqual(bytecode_file.read(4), source_timestamp) self.assertEqual(bytecode_file.read(4), source_timestamp)
# [bad marshal] # [bad marshal]
@source_util.writes_bytecode_files
def test_bad_marshal(self): def test_bad_marshal(self):
with source_util.create_modules('_temp') as mapping: with source_util.create_modules('_temp') as mapping:
bytecode_path = source_util.bytecode_path(mapping['_temp']) bytecode_path = source_util.bytecode_path(mapping['_temp'])
@ -172,6 +178,26 @@ class BadBytecodeTest(unittest.TestCase):
self.import_(mapping['_temp'], '_temp') self.import_(mapping['_temp'], '_temp')
self.assertTrue('_temp' not in sys.modules) self.assertTrue('_temp' not in sys.modules)
# [bytecode read-only]
@source_util.writes_bytecode_files
def test_read_only_bytecode(self):
with source_util.create_modules('_temp') as mapping:
# Create bytecode that will need to be re-created.
py_compile.compile(mapping['_temp'])
bytecode_path = source_util.bytecode_path(mapping['_temp'])
with open(bytecode_path, 'r+b') as bytecode_file:
bytecode_file.seek(0)
bytecode_file.write(b'\x00\x00\x00\x00')
# Make the bytecode read-only.
os.chmod(bytecode_path,
stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
try:
# Should not raise IOError!
self.import_(mapping['_temp'], '_temp')
finally:
# Make writable for eventual clean-up.
os.chmod(bytecode_path, stat.S_IWUSR)
def test_main(): def test_main():
from test.support import run_unittest from test.support import run_unittest

View File

@ -472,6 +472,7 @@ Nick Maclaren
Don MacMillen Don MacMillen
Steve Majewski Steve Majewski
Grzegorz Makarewicz Grzegorz Makarewicz
Dave Malcolm
Ken Manheimer Ken Manheimer
Vladimir Marangozov Vladimir Marangozov
David Marek David Marek

View File

@ -123,6 +123,9 @@ C-API
Library Library
------- -------
- Issue #7187: Importlib would not silence the IOError raised when trying to
write new bytecode when it was made read-only.
- Issue #7264: Fix a possible deadlock when deallocating thread-local objects - Issue #7264: Fix a possible deadlock when deallocating thread-local objects
which are part of a reference cycle. which are part of a reference cycle.