Skip to content
Permalink
Browse files

[3.8] bpo-37409: fix relative import with no parent (GH-14956) (GH-15913

)

Relative imports use resolve_name to get the absolute target name,
which first seeks the current module's absolute package name from the globals:
If __package__ (and __spec__.parent) are missing then
import uses __name__, truncating the last segment if
the module is a submodule rather than a package __init__.py
(which it guesses from whether __path__ is defined).

The __name__ attempt should fail if there is no parent package (top level modules),
if __name__ is '__main__' (-m entry points), or both (scripts).
That is, if both __name__ has no subcomponents and the module does not seem
to be a package __init__ module then import should fail..
(cherry picked from commit 92420b3)

Co-authored-by: Ben Lewis <benjimin@users.noreply.github.com>
  • Loading branch information
brettcannon and benjimin committed Sep 11, 2019
1 parent 0ba5dbd commit 0a6693a469cfb1dd5c8048d8cb4231a7b5883251
@@ -160,6 +160,10 @@ def test_import(self):
self.assertRaises(TypeError, __import__, 1, 2, 3, 4)
self.assertRaises(ValueError, __import__, '')
self.assertRaises(TypeError, __import__, 'sys', name='sys')
# Relative import outside of a package with no __package__ or __spec__ (bpo-37409).
self.assertRaises(ImportError, __import__, '',
{'__package__': None, '__spec__': None, '__name__': '__main__'},
locals={}, fromlist=('foo',), level=1)
# embedded null character
self.assertRaises(ModuleNotFoundError, __import__, 'string\x00')

@@ -774,6 +774,11 @@ def check_relative():
ns = dict(__package__=object())
self.assertRaises(TypeError, check_relative)

def test_parentless_import_shadowed_by_global(self):
# Test as if this were done from the REPL where this error most commonly occurs (bpo-37409).
script_helper.assert_python_failure('-W', 'ignore', '-c',
"foo = 1; from . import foo")

def test_absolute_import_without_future(self):
# If explicit relative import syntax is used, then do not try
# to perform an absolute import in the face of failure.
@@ -969,6 +969,7 @@ Alain Leufroy
Mark Levinson
Mark Levitt
Ivan Levkivskyi
Ben Lewis
William Lewis
Akira Li
Robert Li
@@ -0,0 +1,2 @@
Ensure explicit relative imports from interactive sessions and scripts (having no parent package) always raise ImportError, rather than treating the current module as the package.
Patch by Ben Lewis.
@@ -1601,22 +1601,20 @@ resolve_name(PyObject *name, PyObject *globals, int level)
if (dot == -2) {
goto error;
}

if (dot >= 0) {
PyObject *substr = PyUnicode_Substring(package, 0, dot);
if (substr == NULL) {
goto error;
}
Py_SETREF(package, substr);
else if (dot == -1) {
goto no_parent_error;
}
PyObject *substr = PyUnicode_Substring(package, 0, dot);
if (substr == NULL) {
goto error;
}
Py_SETREF(package, substr);
}
}

last_dot = PyUnicode_GET_LENGTH(package);
if (last_dot == 0) {
PyErr_SetString(PyExc_ImportError,
"attempted relative import with no known parent package");
goto error;
goto no_parent_error;
}

for (level_up = 1; level_up < level; level_up += 1) {
@@ -1642,6 +1640,11 @@ resolve_name(PyObject *name, PyObject *globals, int level)
Py_DECREF(base);
return abs_name;

no_parent_error:
PyErr_SetString(PyExc_ImportError,
"attempted relative import "
"with no known parent package");

error:
Py_XDECREF(package);
return NULL;

0 comments on commit 0a6693a

Please sign in to comment.
You can’t perform that action at this time.