Issue #6074: Apply an appropriate fix for importlib based imports

This commit is contained in:
Nick Coghlan 2012-10-19 23:32:00 +10:00
parent 90eb8ae6ce
commit eb8d627bbd
4 changed files with 2008 additions and 1943 deletions

View File

@ -1048,6 +1048,9 @@ class SourceFileLoader(FileLoader, SourceLoader):
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
return self.set_data(bytecode_path, data, _mode=mode)
def set_data(self, path, data, *, _mode=0o666):

View File

@ -14,6 +14,8 @@ import unittest
import textwrap
import errno
import shutil
import contextlib
import time
import test.support
from test.support import (
@ -33,6 +35,24 @@ def remove_files(name):
rmtree('__pycache__')
@contextlib.contextmanager
def _ready_to_import(name=None, source=""):
# sets up a temporary directory and removes it
# creates the module file
# temporarily clears the module from sys.modules (if any)
name = name or "spam"
with script_helper.temp_dir() as tempdir:
path = script_helper.make_script(tempdir, name, source)
old_module = sys.modules.pop(name, None)
try:
sys.path.insert(0, tempdir)
yield name, path
sys.path.remove(tempdir)
finally:
if old_module is not None:
sys.modules[name] = old_module
class ImportTests(unittest.TestCase):
def setUp(self):
@ -101,54 +121,6 @@ class ImportTests(unittest.TestCase):
finally:
del sys.path[0]
@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
def test_creation_mode(self):
mask = 0o022
with temp_umask(mask):
sys.path.insert(0, os.curdir)
try:
fname = TESTFN + os.extsep + "py"
create_empty_file(fname)
fn = imp.cache_from_source(fname)
unlink(fn)
importlib.invalidate_caches()
__import__(TESTFN)
if not os.path.exists(fn):
self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file")
s = os.stat(fn)
# Check that the umask is respected, and the executable bits
# aren't set.
self.assertEqual(oct(stat.S_IMODE(s.st_mode)), oct(0o666 & ~mask))
finally:
del sys.path[0]
remove_files(TESTFN)
unload(TESTFN)
@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
def test_cached_mode_issue_2051(self):
mode = 0o600
source = TESTFN + ".py"
with script_helper.temp_dir() as tempdir:
path = script_helper.make_script(tempdir, TESTFN,
"key='top secret'")
os.chmod(path, mode)
compiled = imp.cache_from_source(path)
sys.path.insert(0, tempdir)
try:
__import__(TESTFN)
finally:
sys.path.remove(tempdir)
if not os.path.exists(compiled):
self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file")
stat_info = os.stat(compiled)
self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(mode))
def test_bug7732(self):
source = TESTFN + '.py'
os.mkdir(source)
@ -345,6 +317,92 @@ class ImportTests(unittest.TestCase):
self.fail("fromlist must allow bogus names")
class FilePermissionTests(unittest.TestCase):
# tests for file mode on cached .pyc/.pyo files
@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
def test_creation_mode(self):
mask = 0o022
with temp_umask(mask), _ready_to_import() as (name, path):
cached_path = imp.cache_from_source(path)
module = __import__(name)
if not os.path.exists(cached_path):
self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file")
stat_info = os.stat(cached_path)
# Check that the umask is respected, and the executable bits
# aren't set.
self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)),
oct(0o666 & ~mask))
@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
def test_cached_mode_issue_2051(self):
# permissions of .pyc should match those of .py, regardless of mask
mode = 0o600
with temp_umask(0o022), _ready_to_import() as (name, path):
cached_path = imp.cache_from_source(path)
os.chmod(path, mode)
__import__(name)
if not os.path.exists(cached_path):
self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file")
stat_info = os.stat(cached_path)
self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(mode))
@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
def test_cached_readonly(self):
mode = 0o400
with temp_umask(0o022), _ready_to_import() as (name, path):
cached_path = imp.cache_from_source(path)
os.chmod(path, mode)
__import__(name)
if not os.path.exists(cached_path):
self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file")
stat_info = os.stat(cached_path)
expected = mode | 0o200 # Account for fix for issue #6074
self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(expected))
def test_pyc_always_writable(self):
# Initially read-only .pyc files on Windows used to cause problems
# with later updates, see issue #6074 for details
with _ready_to_import() as (name, path):
# Write a Python file, make it read-only and import it
with open(path, 'w') as f:
f.write("x = 'original'\n")
# Tweak the mtime of the source to ensure pyc gets updated later
s = os.stat(path)
os.utime(path, (s.st_atime, s.st_mtime-100000000))
os.chmod(path, 0o400)
m = __import__(name)
self.assertEqual(m.x, 'original')
# Change the file and then reimport it
os.chmod(path, 0o600)
with open(path, 'w') as f:
f.write("x = 'rewritten'\n")
unload(name)
importlib.invalidate_caches()
m = __import__(name)
self.assertEqual(m.x, 'rewritten')
# Now delete the source file and check the pyc was rewritten
unlink(path)
unload(name)
importlib.invalidate_caches()
if __debug__:
bytecode_only = path + "c"
else:
bytecode_only = path + "o"
os.rename(imp.cache_from_source(path), bytecode_only)
m = __import__(name)
self.assertEqual(m.x, 'rewritten')
class PycRewritingTests(unittest.TestCase):
# Test that the `co_filename` attribute on code objects always points
# to the right file, even when various things happen (e.g. both the .py
@ -945,7 +1003,7 @@ class ImportTracebackTests(unittest.TestCase):
def test_main(verbose=None):
run_unittest(ImportTests, PycacheTests,
run_unittest(ImportTests, PycacheTests, FilePermissionTests,
PycRewritingTests, PathsTests, RelativeImportTests,
OverridingImportBuiltinTests,
ImportlibBootstrapTests,

View File

@ -12,6 +12,9 @@ What's New in Python 3.3.1?
Core and Builtins
-----------------
- Issue #6074: Ensure cached bytecode files can always be updated by the
user that created them, even when the source file is read-only.
- Issue #14783: Improve int() docstring and switch docstrings for str(),
range(), and slice() to use multi-line signatures.

File diff suppressed because it is too large Load Diff