Kaydet (Commit) 19a2f596 authored tarafından Brett Cannon's avatar Brett Cannon

Issue #15056: imp.cache_from_source() and source_from_cache() raise

NotimplementedError when sys.implementation.cache_tag is None.

Thanks to Pranav Ravichandran for taking an initial stab at the patch.
üst bf7eab07
...@@ -180,14 +180,19 @@ file paths. ...@@ -180,14 +180,19 @@ file paths.
source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return
value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2. value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2.
The ``cpython-32`` string comes from the current magic tag (see The ``cpython-32`` string comes from the current magic tag (see
:func:`get_tag`). The returned path will end in ``.pyc`` when :func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then
``__debug__`` is True or ``.pyo`` for an optimized Python :exc:`NotImplementedError` will be raised). The returned path will end in
``.pyc`` when ``__debug__`` is True or ``.pyo`` for an optimized Python
(i.e. ``__debug__`` is False). By passing in True or False for (i.e. ``__debug__`` is False). By passing in True or False for
*debug_override* you can override the system's value for ``__debug__`` for *debug_override* you can override the system's value for ``__debug__`` for
extension selection. extension selection.
*path* need not exist. *path* need not exist.
.. versionchanged:: 3.3
If :attr:`sys.implementation.cache_tag` is ``None``, then
:exc:`NotImplementedError` is raised.
.. function:: source_from_cache(path) .. function:: source_from_cache(path)
...@@ -195,7 +200,13 @@ file paths. ...@@ -195,7 +200,13 @@ file paths.
file path. For example, if *path* is file path. For example, if *path* is
``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be
``/foo/bar/baz.py``. *path* need not exist, however if it does not conform ``/foo/bar/baz.py``. *path* need not exist, however if it does not conform
to :pep:`3147` format, a ``ValueError`` is raised. to :pep:`3147` format, a ``ValueError`` is raised. If
:attr:`sys.implementation.cache_tag` is not defined,
:exc:`NotImplementedError` is raised.
.. versionchanged:: 3.3
Raise :exc:`NotImplementedError` when
:attr:`sys.implementation.cache_tag` is not defined.
.. function:: get_tag() .. function:: get_tag()
...@@ -203,6 +214,10 @@ file paths. ...@@ -203,6 +214,10 @@ file paths.
Return the :pep:`3147` magic tag string matching this version of Python's Return the :pep:`3147` magic tag string matching this version of Python's
magic number, as returned by :func:`get_magic`. magic number, as returned by :func:`get_magic`.
.. note::
You may use :attr:`sys.implementation.cache_tag` directly starting
in Python 3.3.
The following functions help interact with the import system's internal The following functions help interact with the import system's internal
locking mechanism. Locking semantics of imports are an implementation locking mechanism. Locking semantics of imports are an implementation
......
...@@ -58,9 +58,12 @@ def source_from_cache(path): ...@@ -58,9 +58,12 @@ def source_from_cache(path):
The .pyc/.pyo file does not need to exist; this simply returns the path to The .pyc/.pyo file does not need to exist; this simply returns the path to
the .py file calculated to correspond to the .pyc/.pyo file. If path does the .py file calculated to correspond to the .pyc/.pyo file. If path does
not conform to PEP 3147 format, ValueError will be raised. not conform to PEP 3147 format, ValueError will be raised. If
sys.implementation.cache_tag is None then NotImplementedError is raised.
""" """
if sys.implementation.cache_tag is None:
raise NotImplementedError('sys.implementation.cache_tag is None')
head, pycache_filename = os.path.split(path) head, pycache_filename = os.path.split(path)
head, pycache = os.path.split(head) head, pycache = os.path.split(head)
if pycache != _bootstrap._PYCACHE: if pycache != _bootstrap._PYCACHE:
......
...@@ -321,6 +321,8 @@ def cache_from_source(path, debug_override=None): ...@@ -321,6 +321,8 @@ def cache_from_source(path, debug_override=None):
If debug_override is not None, then it must be a boolean and is taken as If debug_override is not None, then it must be a boolean and is taken as
the value of __debug__ instead. the value of __debug__ instead.
If sys.implementation.cache_tag is None then NotImplementedError is raised.
""" """
debug = __debug__ if debug_override is None else debug_override debug = __debug__ if debug_override is None else debug_override
if debug: if debug:
...@@ -329,7 +331,10 @@ def cache_from_source(path, debug_override=None): ...@@ -329,7 +331,10 @@ def cache_from_source(path, debug_override=None):
suffixes = OPTIMIZED_BYTECODE_SUFFIXES suffixes = OPTIMIZED_BYTECODE_SUFFIXES
head, tail = _path_split(path) head, tail = _path_split(path)
base_filename, sep, _ = tail.partition('.') base_filename, sep, _ = tail.partition('.')
filename = ''.join([base_filename, sep, _TAG, suffixes[0]]) tag = sys.implementation.cache_tag
if tag is None:
raise NotImplementedError('sys.implementation.cache_tag is None')
filename = ''.join([base_filename, sep, tag, suffixes[0]])
return _path_join(head, _PYCACHE, filename) return _path_join(head, _PYCACHE, filename)
...@@ -649,7 +654,10 @@ class _LoaderBasics: ...@@ -649,7 +654,10 @@ class _LoaderBasics:
code_object = self.get_code(name) code_object = self.get_code(name)
module.__file__ = self.get_filename(name) module.__file__ = self.get_filename(name)
if not sourceless: if not sourceless:
try:
module.__cached__ = cache_from_source(module.__file__) module.__cached__ = cache_from_source(module.__file__)
except NotImplementedError:
module.__cached__ = module.__file__
else: else:
module.__cached__ = module.__file__ module.__cached__ = module.__file__
module.__package__ = name module.__package__ = name
...@@ -718,9 +726,12 @@ class SourceLoader(_LoaderBasics): ...@@ -718,9 +726,12 @@ class SourceLoader(_LoaderBasics):
""" """
source_path = self.get_filename(fullname) source_path = self.get_filename(fullname)
bytecode_path = cache_from_source(source_path)
source_mtime = None source_mtime = None
if bytecode_path is not None: try:
bytecode_path = cache_from_source(source_path)
except NotImplementedError:
bytecode_path = None
else:
try: try:
st = self.path_stats(source_path) st = self.path_stats(source_path)
except NotImplementedError: except NotImplementedError:
...@@ -1417,7 +1428,6 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0): ...@@ -1417,7 +1428,6 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0):
_MAGIC_NUMBER = None # Set in _setup() _MAGIC_NUMBER = None # Set in _setup()
_TAG = None # Set in _setup()
def _setup(sys_module, _imp_module): def _setup(sys_module, _imp_module):
...@@ -1479,7 +1489,6 @@ def _setup(sys_module, _imp_module): ...@@ -1479,7 +1489,6 @@ def _setup(sys_module, _imp_module):
# Constants # Constants
setattr(self_module, '_relax_case', _make_relax_case()) setattr(self_module, '_relax_case', _make_relax_case())
setattr(self_module, '_MAGIC_NUMBER', _imp_module.get_magic()) setattr(self_module, '_MAGIC_NUMBER', _imp_module.get_magic())
setattr(self_module, '_TAG', sys.implementation.cache_tag)
if builtin_os == 'nt': if builtin_os == 'nt':
SOURCE_SUFFIXES.append('.pyw') SOURCE_SUFFIXES.append('.pyw')
......
...@@ -231,6 +231,8 @@ class PEP3147Tests(unittest.TestCase): ...@@ -231,6 +231,8 @@ class PEP3147Tests(unittest.TestCase):
tag = imp.get_tag() tag = imp.get_tag()
@unittest.skipUnless(sys.implementation.cache_tag is not None,
'requires sys.implementation.cache_tag not be None')
def test_cache_from_source(self): def test_cache_from_source(self):
# Given the path to a .py file, return the path to its PEP 3147 # Given the path to a .py file, return the path to its PEP 3147
# defined .pyc file (i.e. under __pycache__). # defined .pyc file (i.e. under __pycache__).
...@@ -239,6 +241,12 @@ class PEP3147Tests(unittest.TestCase): ...@@ -239,6 +241,12 @@ class PEP3147Tests(unittest.TestCase):
'qux.{}.pyc'.format(self.tag)) 'qux.{}.pyc'.format(self.tag))
self.assertEqual(imp.cache_from_source(path, True), expect) self.assertEqual(imp.cache_from_source(path, True), expect)
def test_cache_from_source_no_cache_tag(self):
# Non cache tag means NotImplementedError.
with support.swap_attr(sys.implementation, 'cache_tag', None):
with self.assertRaises(NotImplementedError):
imp.cache_from_source('whatever.py')
def test_cache_from_source_no_dot(self): def test_cache_from_source_no_dot(self):
# Directory with a dot, filename without dot. # Directory with a dot, filename without dot.
path = os.path.join('foo.bar', 'file') path = os.path.join('foo.bar', 'file')
...@@ -283,6 +291,9 @@ class PEP3147Tests(unittest.TestCase): ...@@ -283,6 +291,9 @@ class PEP3147Tests(unittest.TestCase):
imp.cache_from_source('\\foo\\bar\\baz/qux.py', True), imp.cache_from_source('\\foo\\bar\\baz/qux.py', True),
'\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag)) '\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag))
@unittest.skipUnless(sys.implementation.cache_tag is not None,
'requires sys.implementation.cache_tag to not be '
'None')
def test_source_from_cache(self): def test_source_from_cache(self):
# Given the path to a PEP 3147 defined .pyc file, return the path to # Given the path to a PEP 3147 defined .pyc file, return the path to
# its source. This tests the good path. # its source. This tests the good path.
...@@ -291,6 +302,13 @@ class PEP3147Tests(unittest.TestCase): ...@@ -291,6 +302,13 @@ class PEP3147Tests(unittest.TestCase):
expect = os.path.join('foo', 'bar', 'baz', 'qux.py') expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
self.assertEqual(imp.source_from_cache(path), expect) self.assertEqual(imp.source_from_cache(path), expect)
def test_source_from_cache_no_cache_tag(self):
# If sys.implementation.cache_tag is None, raise NotImplementedError.
path = os.path.join('blah', '__pycache__', 'whatever.pyc')
with support.swap_attr(sys.implementation, 'cache_tag', None):
with self.assertRaises(NotImplementedError):
imp.source_from_cache(path)
def test_source_from_cache_bad_path(self): def test_source_from_cache_bad_path(self):
# When the path to a pyc file is not in PEP 3147 format, a ValueError # When the path to a pyc file is not in PEP 3147 format, a ValueError
# is raised. # is raised.
......
...@@ -31,6 +31,9 @@ Core and Builtins ...@@ -31,6 +31,9 @@ Core and Builtins
Library Library
------- -------
- Issue #15056: imp.cache_from_source() and source_from_cache() raise
NotImplementedError when sys.implementation.cache_tag is set to None.
- Issue #15256: Grammatical mistake in exception raised by imp.find_module(). - Issue #15256: Grammatical mistake in exception raised by imp.find_module().
- Issue #5931: wsgiref environ variable SERVER_SOFTWARE will specify an - Issue #5931: wsgiref environ variable SERVER_SOFTWARE will specify an
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment