Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e0d88a1

Browse files
committed
Issue #14605: Make explicit the entries on sys.path_hooks that used to
be implicit. Added a warning for when sys.path_hooks is found to be empty. Also changed the meaning of None in sys.path_importer_cache to represent trying sys.path_hooks again (an interpretation of previous semantics). Also added a warning for when None was found. The long-term goal is for None in sys.path_importer_cache to represent the same as imp.NullImporter: no finder found for that sys.path entry.
1 parent 8f79dd5 commit e0d88a1

9 files changed

Lines changed: 3054 additions & 3185 deletions

File tree

Lib/importlib/_bootstrap.py

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -752,15 +752,15 @@ class PathFinder:
752752
"""Meta path finder for sys.(path|path_hooks|path_importer_cache)."""
753753

754754
@classmethod
755-
def _path_hooks(cls, path, hooks=None):
755+
def _path_hooks(cls, path):
756756
"""Search sequence of hooks for a finder for 'path'.
757757
758758
If 'hooks' is false then use sys.path_hooks.
759759
760760
"""
761-
if hooks is None:
762-
hooks = sys.path_hooks
763-
for hook in hooks:
761+
if not sys.path_hooks:
762+
_warnings.warn('sys.path_hooks is empty', ImportWarning)
763+
for hook in sys.path_hooks:
764764
try:
765765
return hook(path)
766766
except ImportError:
@@ -770,14 +770,11 @@ def _path_hooks(cls, path, hooks=None):
770770
path=path)
771771

772772
@classmethod
773-
def _path_importer_cache(cls, path, default=None):
773+
def _path_importer_cache(cls, path):
774774
"""Get the finder for the path from sys.path_importer_cache.
775775
776776
If the path is not in the cache, find the appropriate finder and cache
777-
it. If None is cached, get the default finder and cache that
778-
(if applicable).
779-
780-
Because of NullImporter, some finder should be returned. The only
777+
it. Because of NullImporter, some finder should be returned. The only
781778
explicit fail case is if None is cached but the path cannot be used for
782779
the default hook, for which ImportError is raised.
783780
@@ -790,9 +787,13 @@ def _path_importer_cache(cls, path, default=None):
790787
finder = cls._path_hooks(path)
791788
sys.path_importer_cache[path] = finder
792789
else:
793-
if finder is None and default:
794-
# Raises ImportError on failure.
795-
finder = default(path)
790+
if finder is None:
791+
msg = ("'None' in sys.path_importer_cache[{!r}], so retrying "
792+
"finder search; in future versions of Python 'None' "
793+
"will represent no finder".format(path))
794+
_warnings.warn(msg, ImportWarning)
795+
del sys.path_importer_cache[path]
796+
finder = cls._path_hooks(path)
796797
sys.path_importer_cache[path] = finder
797798
return finder
798799

@@ -931,29 +932,6 @@ def path_hook_for_FileFinder(path):
931932

932933
# Import itself ###############################################################
933934

934-
_DEFAULT_PATH_HOOK = None # Set in _setup()
935-
936-
class _DefaultPathFinder(PathFinder):
937-
938-
"""Subclass of PathFinder that implements implicit semantics for
939-
__import__."""
940-
941-
@classmethod
942-
def _path_hooks(cls, path):
943-
"""Search sys.path_hooks as well as implicit path hooks."""
944-
try:
945-
return super()._path_hooks(path)
946-
except ImportError:
947-
implicit_hooks = [_DEFAULT_PATH_HOOK, _imp.NullImporter]
948-
return super()._path_hooks(path, implicit_hooks)
949-
950-
@classmethod
951-
def _path_importer_cache(cls, path):
952-
"""Use the default path hook when None is stored in
953-
sys.path_importer_cache."""
954-
return super()._path_importer_cache(path, _DEFAULT_PATH_HOOK)
955-
956-
957935
class _ImportLockContext:
958936

959937
"""Context manager for the import lock."""
@@ -1008,7 +986,7 @@ def _sanity_check(name, package, level):
1008986
raise ValueError("Empty module name")
1009987

1010988

1011-
_IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, _DefaultPathFinder]
989+
_IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, PathFinder]
1012990

1013991
_ERR_MSG = 'No module named {!r}'
1014992

@@ -1203,12 +1181,6 @@ def _setup(sys_module, _imp_module):
12031181
if builtin_os == 'nt':
12041182
SOURCE_SUFFIXES.append('.pyw')
12051183

1206-
supported_loaders = [(ExtensionFileLoader, _suffix_list(3), False),
1207-
(SourceFileLoader, _suffix_list(1), True),
1208-
(SourcelessFileLoader, _suffix_list(2), True)]
1209-
setattr(self_module, '_DEFAULT_PATH_HOOK',
1210-
FileFinder.path_hook(*supported_loaders))
1211-
12121184

12131185
def _install(sys_module, _imp_module):
12141186
"""Install importlib as the implementation of import.
@@ -1218,6 +1190,8 @@ def _install(sys_module, _imp_module):
12181190
12191191
"""
12201192
_setup(sys_module, _imp_module)
1221-
orig_import = builtins.__import__
1222-
builtins.__import__ = __import__
1223-
builtins.__original_import__ = orig_import
1193+
supported_loaders = [(ExtensionFileLoader, _suffix_list(3), False),
1194+
(SourceFileLoader, _suffix_list(1), True),
1195+
(SourcelessFileLoader, _suffix_list(2), True)]
1196+
sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders),
1197+
_imp.NullImporter])

Lib/importlib/test/import_/test_fromlist.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Test that the semantics relating to the 'fromlist' argument are correct."""
22
from .. import util
33
from . import util as import_util
4+
import imp
45
import unittest
56

67
class ReturnValue(unittest.TestCase):
@@ -73,7 +74,8 @@ def test_module_from_package(self):
7374
def test_no_module_from_package(self):
7475
# [no module]
7576
with util.mock_modules('pkg.__init__') as importer:
76-
with util.import_state(meta_path=[importer]):
77+
with util.import_state(meta_path=[importer],
78+
path_hooks=[imp.NullImporter]):
7779
module = import_util.import_('pkg', fromlist='non_existent')
7880
self.assertEqual(module.__name__, 'pkg')
7981
self.assertTrue(not hasattr(module, 'non_existent'))

Lib/importlib/test/import_/test_path.py

Lines changed: 24 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from test import support
1010
from types import MethodType
1111
import unittest
12+
import warnings
1213

1314

1415
class FinderTests(unittest.TestCase):
@@ -64,12 +65,18 @@ def test_path_hooks(self):
6465
self.assertTrue(path in sys.path_importer_cache)
6566
self.assertTrue(sys.path_importer_cache[path] is importer)
6667

67-
def test_path_importer_cache_has_None(self):
68-
# Test that if sys.path_importer_cache has None that None is returned.
69-
clear_cache = {path: None for path in sys.path}
70-
with util.import_state(path_importer_cache=clear_cache):
71-
for name in ('asynchat', 'sys', '<test module>'):
72-
self.assertTrue(machinery.PathFinder.find_module(name) is None)
68+
def test_empty_path_hooks(self):
69+
# Test that if sys.path_hooks is empty a warning is raised and
70+
# PathFinder returns None.
71+
# tried again (with a warning).
72+
with util.import_state(path_importer_cache={}, path_hooks=[],
73+
path=['bogus_path']):
74+
with warnings.catch_warnings(record=True) as w:
75+
warnings.simplefilter('always')
76+
self.assertIsNone(machinery.PathFinder.find_module('os'))
77+
self.assertNotIn('os', sys.path_importer_cache)
78+
self.assertEqual(len(w), 1)
79+
self.assertTrue(issubclass(w[-1].category, ImportWarning))
7380

7481
def test_path_importer_cache_has_None_continues(self):
7582
# Test that having None in sys.path_importer_cache causes the search to
@@ -78,9 +85,16 @@ def test_path_importer_cache_has_None_continues(self):
7885
module = '<test module>'
7986
importer = util.mock_modules(module)
8087
with util.import_state(path=['1', '2'],
81-
path_importer_cache={'1': None, '2': importer}):
82-
loader = machinery.PathFinder.find_module(module)
83-
self.assertTrue(loader is importer)
88+
path_importer_cache={'1': None, '2': importer},
89+
path_hooks=[imp.NullImporter]):
90+
with warnings.catch_warnings(record=True) as w:
91+
warnings.simplefilter('always')
92+
loader = machinery.PathFinder.find_module(module)
93+
self.assertTrue(loader is importer)
94+
self.assertEqual(len(w), 1)
95+
warned = w[0]
96+
self.assertTrue(issubclass(warned.category, ImportWarning))
97+
self.assertIn(repr(None), str(warned.message))
8498

8599
def test_path_importer_cache_empty_string(self):
86100
# The empty string should create a finder using the cwd.
@@ -94,57 +108,9 @@ def test_path_importer_cache_empty_string(self):
94108
self.assertIn(os.curdir, sys.path_importer_cache)
95109

96110

97-
class DefaultPathFinderTests(unittest.TestCase):
98-
99-
"""Test _bootstrap._DefaultPathFinder."""
100-
101-
def test_implicit_hooks(self):
102-
# Test that the implicit path hooks are used.
103-
bad_path = '<path>'
104-
module = '<module>'
105-
assert not os.path.exists(bad_path)
106-
existing_path = tempfile.mkdtemp()
107-
try:
108-
with util.import_state():
109-
nothing = _bootstrap._DefaultPathFinder.find_module(module,
110-
path=[existing_path])
111-
self.assertTrue(nothing is None)
112-
self.assertTrue(existing_path in sys.path_importer_cache)
113-
result = isinstance(sys.path_importer_cache[existing_path],
114-
imp.NullImporter)
115-
self.assertFalse(result)
116-
nothing = _bootstrap._DefaultPathFinder.find_module(module,
117-
path=[bad_path])
118-
self.assertTrue(nothing is None)
119-
self.assertTrue(bad_path in sys.path_importer_cache)
120-
self.assertTrue(isinstance(sys.path_importer_cache[bad_path],
121-
imp.NullImporter))
122-
finally:
123-
os.rmdir(existing_path)
124-
125-
126-
def test_path_importer_cache_has_None(self):
127-
# Test that the default hook is used when sys.path_importer_cache
128-
# contains None for a path.
129-
module = '<test module>'
130-
importer = util.mock_modules(module)
131-
path = '<test path>'
132-
# XXX Not blackbox.
133-
original_hook = _bootstrap._DEFAULT_PATH_HOOK
134-
mock_hook = import_util.mock_path_hook(path, importer=importer)
135-
_bootstrap._DEFAULT_PATH_HOOK = mock_hook
136-
try:
137-
with util.import_state(path_importer_cache={path: None}):
138-
loader = _bootstrap._DefaultPathFinder.find_module(module,
139-
path=[path])
140-
self.assertTrue(loader is importer)
141-
finally:
142-
_bootstrap._DEFAULT_PATH_HOOK = original_hook
143-
144-
145111
def test_main():
146112
from test.support import run_unittest
147-
run_unittest(FinderTests, DefaultPathFinderTests)
113+
run_unittest(FinderTests)
148114

149115
if __name__ == '__main__':
150116
test_main()

Lib/pkgutil.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -379,18 +379,15 @@ def get_importer(path_item):
379379
for path_hook in sys.path_hooks:
380380
try:
381381
importer = path_hook(path_item)
382+
sys.path_importer_cache.setdefault(path_item, importer)
382383
break
383384
except ImportError:
384385
pass
385386
else:
386-
importer = None
387-
sys.path_importer_cache.setdefault(path_item, importer)
388-
389-
if importer is None:
390-
try:
391-
importer = ImpImporter(path_item)
392-
except ImportError:
393-
importer = None
387+
try:
388+
importer = ImpImporter(path_item)
389+
except ImportError:
390+
importer = None
394391
return importer
395392

396393

Lib/runpy.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# Written by Nick Coghlan <ncoghlan at gmail.com>
1010
# to implement PEP 338 (Executing Modules as Scripts)
1111

12+
import os
1213
import sys
1314
import imp
1415
from pkgutil import read_code
@@ -94,7 +95,7 @@ def _get_filename(loader, mod_name):
9495
for attr in ("get_filename", "_get_filename"):
9596
meth = getattr(loader, attr, None)
9697
if meth is not None:
97-
return meth(mod_name)
98+
return os.path.abspath(meth(mod_name))
9899
return None
99100

100101
# Helper to get the loader, code and filename for a module
@@ -198,10 +199,6 @@ def _get_importer(path_name):
198199
try:
199200
importer = cache[path_name]
200201
except KeyError:
201-
# Not yet cached. Flag as using the
202-
# standard machinery until we finish
203-
# checking the hooks
204-
cache[path_name] = None
205202
for hook in sys.path_hooks:
206203
try:
207204
importer = hook(path_name)
@@ -213,10 +210,7 @@ def _get_importer(path_name):
213210
# NullImporter throws ImportError if the supplied path is a
214211
# *valid* directory entry (and hence able to be handled
215212
# by the standard import machinery)
216-
try:
217-
importer = imp.NullImporter(path_name)
218-
except ImportError:
219-
return None
213+
importer = imp.NullImporter(path_name)
220214
cache[path_name] = importer
221215
return importer
222216

Lib/test/test_cmd_line_script.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# tests command line execution of scripts
22

3+
import importlib
34
import unittest
45
import sys
56
import os
@@ -49,12 +50,16 @@ def f():
4950
"""
5051

5152
def _make_test_script(script_dir, script_basename, source=test_source):
52-
return make_script(script_dir, script_basename, source)
53+
to_return = make_script(script_dir, script_basename, source)
54+
importlib.invalidate_caches()
55+
return to_return
5356

5457
def _make_test_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
5558
source=test_source, depth=1):
56-
return make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
57-
source, depth)
59+
to_return = make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
60+
source, depth)
61+
importlib.invalidate_caches()
62+
return to_return
5863

5964
# There's no easy way to pass the script directory in to get
6065
# -m to work (avoiding that is the whole point of making
@@ -72,7 +77,9 @@ def _make_launch_script(script_dir, script_basename, module_name, path=None):
7277
else:
7378
path = repr(path)
7479
source = launch_source % (path, module_name)
75-
return make_script(script_dir, script_basename, source)
80+
to_return = make_script(script_dir, script_basename, source)
81+
importlib.invalidate_caches()
82+
return to_return
7683

7784
class CmdLineTest(unittest.TestCase):
7885
def _check_output(self, script_name, exit_code, data,

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ What's New in Python 3.3.0 Alpha 3?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #14605: No longer have implicit entries in sys.path_hooks. If
14+
sys.path_hooks is found to be empty, a warning will be raised. If None is
15+
found in sys.path_importer_cache, a warning is raised and a search on
16+
sys.path_hooks is attempted.
17+
1318
- Issue #13903: Implement PEP 412. Individual dictionary instances can now share
1419
their keys with other dictionaries. Classes take advantage of this to share
1520
their instance dictionary keys for improved memory and performance.

0 commit comments

Comments
 (0)