diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 007127b9fd4267..7f236940a5be5f 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -204,6 +204,17 @@ def _write_atomic(path, data, mode=0o666): raise +def _fscodec(): + encoding = sys.getfilesystemencoding() + errors = sys.getfilesystemencodeerrors() + + def fsdecode(filename): + return filename.decode(encoding, errors) + + return fsdecode + + +_fsdecode = _fscodec() _code_type = type(_write_atomic.__code__) @@ -1567,6 +1578,9 @@ def __init__(self, path, *loader_details): """Initialize with the path to search on and a variable number of 2-tuples containing the loader and the file suffixes the loader recognizes.""" + if isinstance(path, bytes): + path = _fsdecode(path) + loaders = [] for loader, suffixes in loader_details: loaders.extend((suffix, loader) for suffix in suffixes) diff --git a/Lib/test/test_importlib/import_/test_path.py b/Lib/test/test_importlib/import_/test_path.py index de620842bbc52b..0a4656ba8fed6f 100644 --- a/Lib/test/test_importlib/import_/test_path.py +++ b/Lib/test/test_importlib/import_/test_path.py @@ -1,4 +1,4 @@ -from test.test_importlib import util +from test.test_importlib import util, fixtures importlib = util.import_importlib('importlib') machinery = util.import_importlib('importlib.machinery') @@ -116,6 +116,31 @@ def test_None_on_sys_path(self): if email is not missing: sys.modules['email'] = email + def test_bytes_on_sys_path(self): + # Putting bytes in sys.path[0] caused an import regression from Python + # 3.2: https://bugs.python.org/issue47025 + with fixtures.tempdir() as tmp_path: + (tmp_path / "email.py").write_bytes(b"EXAMPLE = 'bytes_on_sys_path'\n") + new_path = sys.path[:] + new_path.insert(0, os.fsencode(tmp_path)) + new_path_importer_cache = sys.path_importer_cache.copy() + new_path_hooks = [zipimport.zipimporter, + self.machinery.FileFinder.path_hook( + *self.importlib._bootstrap_external._get_supported_file_loaders())] + missing = object() + email = sys.modules.pop('email', missing) + try: + with util.import_state(meta_path=sys.meta_path[:], + path=new_path, + path_importer_cache=new_path_importer_cache, + path_hooks=new_path_hooks): + module = self.importlib.import_module('email') + self.assertIsInstance(module, ModuleType) + self.assertEqual(module.EXAMPLE, "bytes_on_sys_path") + finally: + if email is not missing: + sys.modules['email'] = email + def test_finder_with_find_module(self): class TestFinder: def find_module(self, fullname): diff --git a/Misc/NEWS.d/next/Library/2022-03-15-11-00-55.bpo-47025.oS8dt6.rst b/Misc/NEWS.d/next/Library/2022-03-15-11-00-55.bpo-47025.oS8dt6.rst new file mode 100644 index 00000000000000..ca42e52d71df7c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-03-15-11-00-55.bpo-47025.oS8dt6.rst @@ -0,0 +1 @@ +support bytes on sys.path in FileFinder. Patch by Thomas Grainger.