bpo-37444: Update differing exception between builtins and importlib (GH-14869)



Imports now raise `TypeError` instead of `ValueError` for relative import failures. This makes things consistent between `builtins.__import__` and `importlib.__import__` as well as using a more natural import for the failure.


https://bugs.python.org/issue37444



Automerge-Triggered-By: @brettcannon
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 32deef1..5e2f520 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -873,7 +873,7 @@
     """Resolve a relative module name to an absolute one."""
     bits = package.rsplit('.', level - 1)
     if len(bits) < level:
-        raise ValueError('attempted relative import beyond top-level package')
+        raise ImportError('attempted relative import beyond top-level package')
     base = bits[0]
     return '{}.{}'.format(base, name) if name else base
 
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py
index 201e0f4..269a6fa 100644
--- a/Lib/importlib/util.py
+++ b/Lib/importlib/util.py
@@ -29,8 +29,8 @@
     if not name.startswith('.'):
         return name
     elif not package:
-        raise ValueError(f'no package specified for {repr(name)} '
-                         '(required for relative module names)')
+        raise ImportError(f'no package specified for {repr(name)} '
+                          '(required for relative module names)')
     level = 0
     for character in name:
         if character != '.':
diff --git a/Lib/test/test_importlib/import_/test_relative_imports.py b/Lib/test/test_importlib/import_/test_relative_imports.py
index 8a95a32..586a9bf 100644
--- a/Lib/test/test_importlib/import_/test_relative_imports.py
+++ b/Lib/test/test_importlib/import_/test_relative_imports.py
@@ -156,7 +156,7 @@
                     {'__name__': 'pkg', '__path__': ['blah']})
         def callback(global_):
             self.__import__('pkg')
-            with self.assertRaises(ValueError):
+            with self.assertRaises(ImportError):
                 self.__import__('', global_, fromlist=['top_level'],
                                     level=2)
         self.relative_import_test(create, globals_, callback)
@@ -167,7 +167,7 @@
         globals_ = {'__package__': 'pkg'}, {'__name__': 'pkg.module'}
         def callback(global_):
             self.__import__('pkg')
-            with self.assertRaises(ValueError):
+            with self.assertRaises(ImportError):
                 self.__import__('', global_, fromlist=['top_level'],
                                     level=2)
         self.relative_import_test(create, globals_, callback)
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index 0350a5a..8489c19 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -375,7 +375,7 @@
 
     def test_no_package(self):
         # .bacon in ''
-        with self.assertRaises(ValueError):
+        with self.assertRaises(ImportError):
             self.util.resolve_name('.bacon', '')
 
     def test_in_package(self):
@@ -390,7 +390,7 @@
 
     def test_escape(self):
         # ..bacon in spam
-        with self.assertRaises(ValueError):
+        with self.assertRaises(ImportError):
             self.util.resolve_name('..bacon', 'spam')
 
 
@@ -518,7 +518,7 @@
         with util.temp_module(name, pkg=True) as pkg_dir:
             fullname, _ = util.submodule(name, subname, pkg_dir)
             relname = '.' + subname
-            with self.assertRaises(ValueError):
+            with self.assertRaises(ImportError):
                 self.util.find_spec(relname)
             self.assertNotIn(name, sorted(sys.modules))
             self.assertNotIn(fullname, sorted(sys.modules))