Commit c5fa4494 authored by Ngalim Siregar's avatar Ngalim Siregar Committed by Miss Islington (bot)

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
parent 8e568ef2
......@@ -1433,13 +1433,18 @@ an :term:`importer`.
``importlib.util.resolve_name('sys', __package__)`` without doing a
check to see if the **package** argument is needed.
:exc:`ValueError` is raised if **name** is a relative module name but
package is a false value (e.g. ``None`` or the empty string).
:exc:`ValueError` is also raised a relative name would escape its containing
:exc:`ImportError` is raised if **name** is a relative module name but
**package** is a false value (e.g. ``None`` or the empty string).
:exc:`ImportError` is also raised a relative name would escape its containing
package (e.g. requesting ``..bacon`` from within the ``spam`` package).
.. versionadded:: 3.3
.. versionchanged:: 3.9
To improve consistency with import statements, raise
:exc:`ImportError` instead of :exc:`ValueError` for invalid relative
import attempts.
.. function:: find_spec(name, package=None)
Find the :term:`spec <module spec>` for a module, optionally relative to
......
......@@ -75,6 +75,12 @@ New Features
Other Language Changes
======================
* :func:`builtins.__import__` now raises :exc:`ImportError` instead of
:exc:`ValueError` as used to occur when a relative import went past
its top-level package.
(Contributed by Ngalim Siregar in :issue:`37444`.)
* Python now gets the absolute path of the script filename specified on
the command line (ex: ``python3 script.py``): the ``__file__`` attribute of
the ``__main__`` module, ``sys.argv[0]`` and ``sys.path[0]`` become an
......@@ -118,6 +124,13 @@ pprint
:mod:`pprint` can now pretty-print :class:`types.SimpleNamespace`.
(Contributed by Carl Bordum Hansen in :issue:`37376`.)
importlib
---------
To improve consistency with import statements, :func:`importlib.util.resolve_name`
now raises :exc:`ImportError` instead of :exc:`ValueError` for invalid relative
import attempts.
(Contributed by Ngalim Siregar in :issue:`37444`.)
Optimizations
=============
......@@ -180,4 +193,11 @@ Porting to Python 3.9
This section lists previously described changes and other bugfixes
that may require changes to your code.
Changes in the Python API
-------------------------
* :func:`builtins.__import__` and :func:`importlib.util.resolve_name` now raise
:exc:`ImportError` where it previously raised :exc:`ValueError`. Callers
catching the specific exception type and supporting both Python 3.9 and
earlier versions will need to catch both:
``except (ImportError, ValueError):``
......@@ -873,7 +873,7 @@ def _resolve_name(name, package, level):
"""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
......
......@@ -29,8 +29,8 @@ def resolve_name(name, package):
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 != '.':
......
......@@ -156,7 +156,7 @@ class RelativeImports:
{'__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 @@ class RelativeImports:
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)
......
......@@ -375,7 +375,7 @@ class ResolveNameTests:
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 @@ class ResolveNameTests:
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 @@ class FindSpecTests:
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))
......
Update differing exception between :meth:`builtins.__import__` and :meth:`importlib.__import__`.
......@@ -1671,7 +1671,7 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level
goto error;
}
else if (last_dot == -1) {
_PyErr_SetString(tstate, PyExc_ValueError,
_PyErr_SetString(tstate, PyExc_ImportError,
"attempted relative import beyond top-level "
"package");
goto error;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment