Skip to content

Commit 19a2f59

Browse files
committed
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.
1 parent bf7eab0 commit 19a2f59

File tree

6 files changed

+3842
-3769
lines changed

6 files changed

+3842
-3769
lines changed

Doc/library/imp.rst

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,29 +180,44 @@ file paths.
180180
source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return
181181
value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2.
182182
The ``cpython-32`` string comes from the current magic tag (see
183-
:func:`get_tag`). The returned path will end in ``.pyc`` when
184-
``__debug__`` is True or ``.pyo`` for an optimized Python
183+
:func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then
184+
:exc:`NotImplementedError` will be raised). The returned path will end in
185+
``.pyc`` when ``__debug__`` is True or ``.pyo`` for an optimized Python
185186
(i.e. ``__debug__`` is False). By passing in True or False for
186187
*debug_override* you can override the system's value for ``__debug__`` for
187188
extension selection.
188189

189190
*path* need not exist.
190191

192+
.. versionchanged:: 3.3
193+
If :attr:`sys.implementation.cache_tag` is ``None``, then
194+
:exc:`NotImplementedError` is raised.
195+
191196

192197
.. function:: source_from_cache(path)
193198

194199
Given the *path* to a :pep:`3147` file name, return the associated source code
195200
file path. For example, if *path* is
196201
``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be
197202
``/foo/bar/baz.py``. *path* need not exist, however if it does not conform
198-
to :pep:`3147` format, a ``ValueError`` is raised.
203+
to :pep:`3147` format, a ``ValueError`` is raised. If
204+
:attr:`sys.implementation.cache_tag` is not defined,
205+
:exc:`NotImplementedError` is raised.
206+
207+
.. versionchanged:: 3.3
208+
Raise :exc:`NotImplementedError` when
209+
:attr:`sys.implementation.cache_tag` is not defined.
199210

200211

201212
.. function:: get_tag()
202213

203214
Return the :pep:`3147` magic tag string matching this version of Python's
204215
magic number, as returned by :func:`get_magic`.
205216

217+
.. note::
218+
You may use :attr:`sys.implementation.cache_tag` directly starting
219+
in Python 3.3.
220+
206221

207222
The following functions help interact with the import system's internal
208223
locking mechanism. Locking semantics of imports are an implementation

Lib/imp.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ def source_from_cache(path):
5858
5959
The .pyc/.pyo file does not need to exist; this simply returns the path to
6060
the .py file calculated to correspond to the .pyc/.pyo file. If path does
61-
not conform to PEP 3147 format, ValueError will be raised.
61+
not conform to PEP 3147 format, ValueError will be raised. If
62+
sys.implementation.cache_tag is None then NotImplementedError is raised.
6263
6364
"""
65+
if sys.implementation.cache_tag is None:
66+
raise NotImplementedError('sys.implementation.cache_tag is None')
6467
head, pycache_filename = os.path.split(path)
6568
head, pycache = os.path.split(head)
6669
if pycache != _bootstrap._PYCACHE:

Lib/importlib/_bootstrap.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ def cache_from_source(path, debug_override=None):
321321
If debug_override is not None, then it must be a boolean and is taken as
322322
the value of __debug__ instead.
323323
324+
If sys.implementation.cache_tag is None then NotImplementedError is raised.
325+
324326
"""
325327
debug = __debug__ if debug_override is None else debug_override
326328
if debug:
@@ -329,7 +331,10 @@ def cache_from_source(path, debug_override=None):
329331
suffixes = OPTIMIZED_BYTECODE_SUFFIXES
330332
head, tail = _path_split(path)
331333
base_filename, sep, _ = tail.partition('.')
332-
filename = ''.join([base_filename, sep, _TAG, suffixes[0]])
334+
tag = sys.implementation.cache_tag
335+
if tag is None:
336+
raise NotImplementedError('sys.implementation.cache_tag is None')
337+
filename = ''.join([base_filename, sep, tag, suffixes[0]])
333338
return _path_join(head, _PYCACHE, filename)
334339

335340

@@ -649,7 +654,10 @@ def _load_module(self, module, *, sourceless=False):
649654
code_object = self.get_code(name)
650655
module.__file__ = self.get_filename(name)
651656
if not sourceless:
652-
module.__cached__ = cache_from_source(module.__file__)
657+
try:
658+
module.__cached__ = cache_from_source(module.__file__)
659+
except NotImplementedError:
660+
module.__cached__ = module.__file__
653661
else:
654662
module.__cached__ = module.__file__
655663
module.__package__ = name
@@ -718,9 +726,12 @@ def get_code(self, fullname):
718726
719727
"""
720728
source_path = self.get_filename(fullname)
721-
bytecode_path = cache_from_source(source_path)
722729
source_mtime = None
723-
if bytecode_path is not None:
730+
try:
731+
bytecode_path = cache_from_source(source_path)
732+
except NotImplementedError:
733+
bytecode_path = None
734+
else:
724735
try:
725736
st = self.path_stats(source_path)
726737
except NotImplementedError:
@@ -1417,7 +1428,6 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0):
14171428

14181429

14191430
_MAGIC_NUMBER = None # Set in _setup()
1420-
_TAG = None # Set in _setup()
14211431

14221432

14231433
def _setup(sys_module, _imp_module):
@@ -1479,7 +1489,6 @@ def _setup(sys_module, _imp_module):
14791489
# Constants
14801490
setattr(self_module, '_relax_case', _make_relax_case())
14811491
setattr(self_module, '_MAGIC_NUMBER', _imp_module.get_magic())
1482-
setattr(self_module, '_TAG', sys.implementation.cache_tag)
14831492
if builtin_os == 'nt':
14841493
SOURCE_SUFFIXES.append('.pyw')
14851494

Lib/test/test_imp.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ class PEP3147Tests(unittest.TestCase):
231231

232232
tag = imp.get_tag()
233233

234+
@unittest.skipUnless(sys.implementation.cache_tag is not None,
235+
'requires sys.implementation.cache_tag not be None')
234236
def test_cache_from_source(self):
235237
# Given the path to a .py file, return the path to its PEP 3147
236238
# defined .pyc file (i.e. under __pycache__).
@@ -239,6 +241,12 @@ def test_cache_from_source(self):
239241
'qux.{}.pyc'.format(self.tag))
240242
self.assertEqual(imp.cache_from_source(path, True), expect)
241243

244+
def test_cache_from_source_no_cache_tag(self):
245+
# Non cache tag means NotImplementedError.
246+
with support.swap_attr(sys.implementation, 'cache_tag', None):
247+
with self.assertRaises(NotImplementedError):
248+
imp.cache_from_source('whatever.py')
249+
242250
def test_cache_from_source_no_dot(self):
243251
# Directory with a dot, filename without dot.
244252
path = os.path.join('foo.bar', 'file')
@@ -283,6 +291,9 @@ def test_sep_altsep_and_sep_cache_from_source(self):
283291
imp.cache_from_source('\\foo\\bar\\baz/qux.py', True),
284292
'\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag))
285293

294+
@unittest.skipUnless(sys.implementation.cache_tag is not None,
295+
'requires sys.implementation.cache_tag to not be '
296+
'None')
286297
def test_source_from_cache(self):
287298
# Given the path to a PEP 3147 defined .pyc file, return the path to
288299
# its source. This tests the good path.
@@ -291,6 +302,13 @@ def test_source_from_cache(self):
291302
expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
292303
self.assertEqual(imp.source_from_cache(path), expect)
293304

305+
def test_source_from_cache_no_cache_tag(self):
306+
# If sys.implementation.cache_tag is None, raise NotImplementedError.
307+
path = os.path.join('blah', '__pycache__', 'whatever.pyc')
308+
with support.swap_attr(sys.implementation, 'cache_tag', None):
309+
with self.assertRaises(NotImplementedError):
310+
imp.source_from_cache(path)
311+
294312
def test_source_from_cache_bad_path(self):
295313
# When the path to a pyc file is not in PEP 3147 format, a ValueError
296314
# is raised.

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Core and Builtins
3131
Library
3232
-------
3333

34+
- Issue #15056: imp.cache_from_source() and source_from_cache() raise
35+
NotImplementedError when sys.implementation.cache_tag is set to None.
36+
3437
- Issue #15256: Grammatical mistake in exception raised by imp.find_module().
3538

3639
- Issue #5931: wsgiref environ variable SERVER_SOFTWARE will specify an

0 commit comments

Comments
 (0)