Issue #17907: touch up the code for imp.new_module().
diff --git a/Lib/imp.py b/Lib/imp.py
index e06fbe6..7d54e8a 100644
--- a/Lib/imp.py
+++ b/Lib/imp.py
@@ -17,7 +17,6 @@
load_dynamic = None
# Directly exposed by this module
-from importlib._bootstrap import new_module
from importlib._bootstrap import cache_from_source, source_from_cache
@@ -28,6 +27,7 @@
import os
import sys
import tokenize
+import types
import warnings
@@ -44,6 +44,17 @@
IMP_HOOK = 9
+def new_module(name):
+ """**DEPRECATED**
+
+ Create a new module.
+
+ The module is not entered into sys.modules.
+
+ """
+ return types.ModuleType(name)
+
+
def get_magic():
"""**DEPRECATED**
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 4558054..1276ff1 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -121,15 +121,6 @@
_code_type = type(_wrap.__code__)
-def new_module(name):
- """Create a new module.
-
- The module is not entered into sys.modules.
-
- """
- return type(_io)(name)
-
-
# Module-level locking ########################################################
# A dict mapping module names to weakrefs of _ModuleLock instances
@@ -509,7 +500,7 @@
# This must be done before open() is called as the 'io' module
# implicitly imports 'locale' and would otherwise trigger an
# infinite loop.
- self._module = new_module(self._name)
+ self._module = type(_io)(self._name)
# This must be done before putting the module in sys.modules
# (otherwise an optimization shortcut in import.c becomes wrong)
self._module.__initializing__ = True
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py
index 09ec03c..06f4d2f 100644
--- a/Lib/importlib/util.py
+++ b/Lib/importlib/util.py
@@ -1,9 +1,11 @@
"""Utility code for constructing importers, etc."""
from ._bootstrap import MAGIC_NUMBER
+from ._bootstrap import cache_from_source
from ._bootstrap import module_to_load
from ._bootstrap import set_loader
from ._bootstrap import set_package
+from ._bootstrap import source_from_cache
from ._bootstrap import _resolve_name
import functools
diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py
index 805e9ed..673fb41 100644
--- a/Lib/test/test_import.py
+++ b/Lib/test/test_import.py
@@ -859,7 +859,6 @@
from importlib import machinery
mod = sys.modules['_frozen_importlib']
self.assertIs(machinery.FileFinder, mod.FileFinder)
- self.assertIs(imp.new_module, mod.new_module)
class ImportTracebackTests(unittest.TestCase):
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index 78f5a3e..2c19df4 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -1,6 +1,7 @@
from importlib import util
from . import util as test_util
import imp
+import os
import sys
from test import support
import types
@@ -324,5 +325,120 @@
self.assertTrue(util.MAGIC_NUMBER.endswith(b'\r\n'))
+class PEP3147Tests(unittest.TestCase):
+ """Tests of PEP 3147-related functions:
+ cache_from_source and source_from_cache.
+
+ """
+
+ 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):
+ # Given the path to a .py file, return the path to its PEP 3147
+ # defined .pyc file (i.e. under __pycache__).
+ path = os.path.join('foo', 'bar', 'baz', 'qux.py')
+ expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
+ 'qux.{}.pyc'.format(self.tag))
+ self.assertEqual(util.cache_from_source(path, True), expect)
+
+ def test_cache_from_source_no_cache_tag(self):
+ # No cache tag means NotImplementedError.
+ with support.swap_attr(sys.implementation, 'cache_tag', None):
+ with self.assertRaises(NotImplementedError):
+ util.cache_from_source('whatever.py')
+
+ def test_cache_from_source_no_dot(self):
+ # Directory with a dot, filename without dot.
+ path = os.path.join('foo.bar', 'file')
+ expect = os.path.join('foo.bar', '__pycache__',
+ 'file{}.pyc'.format(self.tag))
+ self.assertEqual(util.cache_from_source(path, True), expect)
+
+ def test_cache_from_source_optimized(self):
+ # Given the path to a .py file, return the path to its PEP 3147
+ # defined .pyo file (i.e. under __pycache__).
+ path = os.path.join('foo', 'bar', 'baz', 'qux.py')
+ expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
+ 'qux.{}.pyo'.format(self.tag))
+ self.assertEqual(util.cache_from_source(path, False), expect)
+
+ def test_cache_from_source_cwd(self):
+ path = 'foo.py'
+ expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
+ self.assertEqual(util.cache_from_source(path, True), expect)
+
+ def test_cache_from_source_override(self):
+ # When debug_override is not None, it can be any true-ish or false-ish
+ # value.
+ path = os.path.join('foo', 'bar', 'baz.py')
+ partial_expect = os.path.join('foo', 'bar', '__pycache__',
+ 'baz.{}.py'.format(self.tag))
+ self.assertEqual(util.cache_from_source(path, []), partial_expect + 'o')
+ self.assertEqual(util.cache_from_source(path, [17]),
+ partial_expect + 'c')
+ # However if the bool-ishness can't be determined, the exception
+ # propagates.
+ class Bearish:
+ def __bool__(self): raise RuntimeError
+ with self.assertRaises(RuntimeError):
+ util.cache_from_source('/foo/bar/baz.py', Bearish())
+
+ @unittest.skipUnless(os.sep == '\\' and os.altsep == '/',
+ 'test meaningful only where os.altsep is defined')
+ def test_sep_altsep_and_sep_cache_from_source(self):
+ # Windows path and PEP 3147 where sep is right of altsep.
+ self.assertEqual(
+ util.cache_from_source('\\foo\\bar\\baz/qux.py', True),
+ '\\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):
+ # Given the path to a PEP 3147 defined .pyc file, return the path to
+ # its source. This tests the good path.
+ path = os.path.join('foo', 'bar', 'baz', '__pycache__',
+ 'qux.{}.pyc'.format(self.tag))
+ expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
+ self.assertEqual(util.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):
+ util.source_from_cache(path)
+
+ def test_source_from_cache_bad_path(self):
+ # When the path to a pyc file is not in PEP 3147 format, a ValueError
+ # is raised.
+ self.assertRaises(
+ ValueError, util.source_from_cache, '/foo/bar/bazqux.pyc')
+
+ def test_source_from_cache_no_slash(self):
+ # No slashes at all in path -> ValueError
+ self.assertRaises(
+ ValueError, util.source_from_cache, 'foo.cpython-32.pyc')
+
+ def test_source_from_cache_too_few_dots(self):
+ # Too few dots in final path component -> ValueError
+ self.assertRaises(
+ ValueError, util.source_from_cache, '__pycache__/foo.pyc')
+
+ def test_source_from_cache_too_many_dots(self):
+ # Too many dots in final path component -> ValueError
+ self.assertRaises(
+ ValueError, util.source_from_cache,
+ '__pycache__/foo.cpython-32.foo.pyc')
+
+ def test_source_from_cache_no__pycache__(self):
+ # Another problem with the path -> ValueError
+ self.assertRaises(
+ ValueError, util.source_from_cache,
+ '/foo/bar/foo.cpython-32.foo.pyc')
+
+
if __name__ == '__main__':
unittest.main()