From effff9ad27951beb989b600a965e65d2289aeb59 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sat, 29 Jun 2019 23:57:16 +1000 Subject: [PATCH 01/14] Regression tests for bpo-37409 --- Lib/test/test_builtin.py | 4 ++++ Lib/test/test_import/__init__.py | 5 +++++ Misc/ACKS | 1 + 3 files changed, 10 insertions(+) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 61155799c44a92..3f473741401352 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -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 with no parent package, issue37409 + self.assertRaises(ImportError, __import__, '', + {'__package__': None, '__name__': '__main__'}, + {}, ('foo',), 1) # embedded null character self.assertRaises(ModuleNotFoundError, __import__, 'string\x00') diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 84cd0da94b36bf..751b9c1653d484 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -775,6 +775,11 @@ def check_relative(): ns = dict(__package__=object()) self.assertRaises(TypeError, check_relative) + def test_import_shadowed_by_global(self): + # Regression test for https://bugs.python.org/issue37409 + 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. diff --git a/Misc/ACKS b/Misc/ACKS index df0810fff46cc6..2072ecf866d2ee 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -964,6 +964,7 @@ Alain Leufroy Mark Levinson Mark Levitt Ivan Levkivskyi +Ben Lewis William Lewis Akira Li Robert Li From 3ec4c49e22339c40656be3beceb8deeeabe0e5a3 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Thu, 25 Jul 2019 00:10:59 +1000 Subject: [PATCH 02/14] bpo-37409: fix builtin/importlib discrepancy for "from . import *" where no package exists 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 as a hail mary it uses __name__, truncating the last segment if the module is any submodule rather than a package __init__.py (which it guesses from whether __path__ is defined). The hail mary should fail if there is no parent package (top level modules), if __name__ is uninformatively '__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. (Bug silently used module as if package, aliasing unexpected objects. Note importlib contained an unaffected alternative __import__ implementation.) --- Python/import.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Python/import.c b/Python/import.c index 3937fbe37d9417..49eea82f6d72d3 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1648,13 +1648,15 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level goto error; } - if (dot >= 0) { - PyObject *substr = PyUnicode_Substring(package, 0, dot); - if (substr == NULL) { - goto error; - } - Py_SETREF(package, substr); + if (dot == -1) { + dot = 0; + } + + PyObject *substr = PyUnicode_Substring(package, 0, dot); + if (substr == NULL) { + goto error; } + Py_SETREF(package, substr); } } From 201c40b178bea6856a22e1f57703444cde6c7cf8 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Mon, 5 Aug 2019 23:19:23 +1000 Subject: [PATCH 03/14] Update Lib/test/test_builtin.py Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Lib/test/test_builtin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 3f473741401352..4722f2819be8c8 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -162,7 +162,7 @@ def test_import(self): self.assertRaises(TypeError, __import__, 'sys', name='sys') # relative import with no parent package, issue37409 self.assertRaises(ImportError, __import__, '', - {'__package__': None, '__name__': '__main__'}, + {'__package__': None, '__spec__': None, '__name__': '__main__'}, {}, ('foo',), 1) # embedded null character self.assertRaises(ModuleNotFoundError, __import__, 'string\x00') From 3fc5481fee7fde000d17112c442124b9a9b0a356 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Mon, 5 Aug 2019 23:19:34 +1000 Subject: [PATCH 04/14] Update Lib/test/test_builtin.py Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Lib/test/test_builtin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 4722f2819be8c8..20d94a2db31d96 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -163,7 +163,7 @@ def test_import(self): # relative import with no parent package, issue37409 self.assertRaises(ImportError, __import__, '', {'__package__': None, '__spec__': None, '__name__': '__main__'}, - {}, ('foo',), 1) + locals={}, fromlist=('foo',), level=1) # embedded null character self.assertRaises(ModuleNotFoundError, __import__, 'string\x00') From 4a3f487791cb4b7aac8b3dae60dd9dfb1246f25c Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Tue, 6 Aug 2019 00:09:09 +1000 Subject: [PATCH 05/14] Update Lib/test/test_import/__init__.py Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Lib/test/test_import/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 751b9c1653d484..288b65f80f675a 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -776,7 +776,7 @@ def check_relative(): self.assertRaises(TypeError, check_relative) def test_import_shadowed_by_global(self): - # Regression test for https://bugs.python.org/issue37409 + # bpo-37409 script_helper.assert_python_failure('-W', 'ignore', '-c', "foo = 1; from . import foo") From c240df50ccc0b4d28a340b02c73f416ae33344b2 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Tue, 6 Aug 2019 00:47:04 +1000 Subject: [PATCH 06/14] Clearer code --- Python/import.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Python/import.c b/Python/import.c index 49eea82f6d72d3..d3b7f58b765781 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1647,11 +1647,9 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level if (dot == -2) { goto error; } - - if (dot == -1) { - dot = 0; + else if (dot == -1) { + goto no_parent_error; } - PyObject *substr = PyUnicode_Substring(package, 0, dot); if (substr == NULL) { goto error; @@ -1662,10 +1660,7 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level last_dot = PyUnicode_GET_LENGTH(package); if (last_dot == 0) { - _PyErr_SetString(tstate, 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) { @@ -1691,7 +1686,13 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level Py_DECREF(base); return abs_name; + no_parent_error: + + _PyErr_SetString(tstate, PyExc_ImportError, + "attempted relative import " + "with no known parent package"); error: + Py_XDECREF(package); return NULL; } From 5090880fe19ad7179b8ec67d60830ab5ef8aa37c Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Wed, 7 Aug 2019 09:12:17 +1000 Subject: [PATCH 07/14] Update Lib/test/test_builtin.py Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Lib/test/test_builtin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 20d94a2db31d96..dea307b5a6b661 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -160,7 +160,7 @@ 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 with no parent package, issue37409 + # 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) From 57b47b7acf4676a7ea42005b95df31fa74c89d6b Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2019 23:39:07 +0000 Subject: [PATCH 08/14] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst b/Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst new file mode 100644 index 00000000000000..d6e08bcfc8c95a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst @@ -0,0 +1 @@ +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. \ No newline at end of file From 6f7f4768965de37a3ea888f15c34fd57e3c89e9b Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Wed, 7 Aug 2019 09:44:05 +1000 Subject: [PATCH 09/14] Clarify test name --- Lib/test/test_import/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 288b65f80f675a..0be3a19e729201 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -775,7 +775,7 @@ def check_relative(): ns = dict(__package__=object()) self.assertRaises(TypeError, check_relative) - def test_import_shadowed_by_global(self): + def test_parentless_import_shadowed_by_global(self): # bpo-37409 script_helper.assert_python_failure('-W', 'ignore', '-c', "foo = 1; from . import foo") From 634fbcbe2cb821a6a6fb495cc0bfdbf6d954f5be Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sat, 10 Aug 2019 23:15:38 +1000 Subject: [PATCH 10/14] Update Python/import.c Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Python/import.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/import.c b/Python/import.c index d3b7f58b765781..f6fad7b3d04e89 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1687,7 +1687,6 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level return abs_name; no_parent_error: - _PyErr_SetString(tstate, PyExc_ImportError, "attempted relative import " "with no known parent package"); From 65d7c2e3b9f89bd8a8ab33fd647f9478e8906737 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sat, 10 Aug 2019 23:15:52 +1000 Subject: [PATCH 11/14] Update Python/import.c Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Python/import.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/import.c b/Python/import.c index f6fad7b3d04e89..8a82abf7bfce58 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1690,6 +1690,7 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level _PyErr_SetString(tstate, PyExc_ImportError, "attempted relative import " "with no known parent package"); + error: Py_XDECREF(package); From 178163c533d9f5689662156b5432c0acc1450951 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sat, 10 Aug 2019 23:16:38 +1000 Subject: [PATCH 12/14] Update Lib/test/test_import/__init__.py Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Lib/test/test_import/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 0be3a19e729201..1ebb11594d10e1 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -776,7 +776,7 @@ def check_relative(): self.assertRaises(TypeError, check_relative) def test_parentless_import_shadowed_by_global(self): - # bpo-37409 + # 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") From fac9e37b994fc51d0d596d58f5bf1dc2ef46656f Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sat, 10 Aug 2019 23:17:00 +1000 Subject: [PATCH 13/14] Update Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- .../Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst b/Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst index d6e08bcfc8c95a..9cfa7154802015 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2019-08-06-23-39-05.bpo-37409.1qwzn2.rst @@ -1 +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. \ No newline at end of file +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. From e0730ff1f94229e1f922245336607b8604b4abf8 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Sat, 10 Aug 2019 23:17:12 +1000 Subject: [PATCH 14/14] Update Python/import.c Co-Authored-By: Brett Cannon <54418+brettcannon@users.noreply.github.com> --- Python/import.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/import.c b/Python/import.c index 8a82abf7bfce58..02f3717d8fe8e4 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1692,7 +1692,6 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level "with no known parent package"); error: - Py_XDECREF(package); return NULL; }