Skip to content

Commit 00e7800

Browse files
committed
Implement disabling imports in try/except and * imports, report errors on bad usage of lazy
1 parent 164423b commit 00e7800

File tree

11 files changed

+99
-27
lines changed

11 files changed

+99
-27
lines changed

Include/internal/pycore_magic_number.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ PC/launcher.c must also be updated.
300300
301301
*/
302302

303-
#define PYC_MAGIC_NUMBER 3656
303+
#define PYC_MAGIC_NUMBER 3657
304304
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
305305
(little-endian) and then appending b'\r\n'. */
306306
#define PYC_MAGIC_NUMBER_TOKEN \

Lib/test/test_import/__init__.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,6 +2662,50 @@ def test_lazy_value_get(self):
26622662

26632663
self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules)
26642664

2665+
def test_lazy_try_except(self):
2666+
with self.assertRaises(SyntaxError):
2667+
import test.test_import.data.lazy_imports.lazy_try_except
2668+
2669+
def test_lazy_try_except_from(self):
2670+
with self.assertRaises(SyntaxError):
2671+
import test.test_import.data.lazy_imports.lazy_try_except_from
2672+
2673+
def test_lazy_try_except_from_star(self):
2674+
with self.assertRaises(SyntaxError):
2675+
import test.test_import.data.lazy_imports.lazy_try_except_from_star
2676+
2677+
def test_try_except_eager(self):
2678+
importlib.set_lazy_imports(True)
2679+
try:
2680+
import test.test_import.data.lazy_imports.try_except_eager
2681+
except ImportError as e:
2682+
self.fail('lazy import failed')
2683+
2684+
self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules)
2685+
2686+
def test_try_except_eager_from(self):
2687+
importlib.set_lazy_imports(True)
2688+
try:
2689+
import test.test_import.data.lazy_imports.try_except_eager_from
2690+
except ImportError as e:
2691+
self.fail('lazy import failed')
2692+
2693+
self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules)
2694+
2695+
def test_lazy_import_func(self):
2696+
with self.assertRaises(SyntaxError):
2697+
import test.test_import.data.lazy_imports.lazy_import_func
2698+
2699+
def test_eager_import_func(self):
2700+
importlib.set_lazy_imports(True)
2701+
try:
2702+
import test.test_import.data.lazy_imports.eager_import_func
2703+
except ImportError as e:
2704+
self.fail('lazy import failed')
2705+
2706+
f = test.test_import.data.lazy_imports.eager_import_func.f
2707+
self.assertEqual(type(f()), type(sys))
2708+
26652709

26662710
class TestSinglePhaseSnapshot(ModuleSnapshot):
26672711
"""A representation of a single-phase init module for testing.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def f():
2+
lazy import foo
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
try:
2+
lazy import foo
3+
except:
4+
pass
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
try:
2+
lazy from foo import bar
3+
except:
4+
pass
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lazy from foo import *
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
try:
2+
import test.test_import.data.lazy_imports.basic2
3+
except:
4+
pass
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
try:
2+
from test.test_import.data.lazy_imports.basic2 import f
3+
except:
4+
pass

Python/ceval.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,17 +3049,6 @@ _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *glob
30493049
break;
30503050
}
30513051

3052-
// Always make star imports eager regardless of lazy setting
3053-
if (fromlist && PyTuple_Check(fromlist) && PyTuple_GET_SIZE(fromlist) == 1) {
3054-
PyObject *item = PyTuple_GET_ITEM(fromlist, 0);
3055-
if (PyUnicode_Check(item)) {
3056-
const char *item_str = PyUnicode_AsUTF8(item);
3057-
if (item_str && strcmp(item_str, "*") == 0) {
3058-
lazy = 0; // Force star imports to be eager
3059-
}
3060-
}
3061-
}
3062-
30633052
if (!lazy) {
30643053
// Not a lazy import or lazy imports are disabled, fallback to the regular import
30653054
return _PyEval_ImportName(tstate, builtins, globals, locals, name, fromlist, level);

Python/codegen.c

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2852,6 +2852,18 @@ codegen_import_as(compiler *c, location loc,
28522852
return codegen_nameop(c, loc, asname, Store);
28532853
}
28542854

2855+
static int
2856+
codegen_validate_lazy_import(compiler *c, location loc)
2857+
{
2858+
if (_PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) {
2859+
return _PyCompile_Error(c, loc, "lazy imports only allowed in module scope");
2860+
} else if (_PyCompile_TopFBlock(c)) {
2861+
return _PyCompile_Error(c, loc, "cannot lazy import in a nested scope");
2862+
}
2863+
2864+
return SUCCESS;
2865+
}
2866+
28552867
static int
28562868
codegen_import(compiler *c, stmt_ty s)
28572869
{
@@ -2873,11 +2885,15 @@ codegen_import(compiler *c, stmt_ty s)
28732885
ADDOP_LOAD_CONST(c, loc, zero);
28742886
ADDOP_LOAD_CONST(c, loc, Py_None);
28752887
if (s->v.Import.is_lazy) {
2876-
// TODO: SyntaxError when not in module scope
2888+
RETURN_IF_ERROR(codegen_validate_lazy_import(c, loc));
28772889
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 1);
28782890
} else {
2879-
// TODO: If in try/except, set 2nd bit
2880-
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 0);
2891+
if (_PyCompile_TopFBlock(c) || _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) {
2892+
// force eager import in try/except block
2893+
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 2);
2894+
} else {
2895+
ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 0);
2896+
}
28812897
}
28822898

28832899
if (alias->asname) {
@@ -2929,11 +2945,23 @@ codegen_from_import(compiler *c, stmt_ty s)
29292945
from = s->v.ImportFrom.module;
29302946
}
29312947
if (s->v.ImportFrom.is_lazy) {
2932-
// TODO: SyntaxError when not in module scope
2948+
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0);
2949+
if (PyUnicode_READ_CHAR(alias->name, 0) == '*') {
2950+
return _PyCompile_Error(c, LOC(s), "cannot lazy import *");
2951+
}
2952+
RETURN_IF_ERROR(codegen_validate_lazy_import(c, LOC(s)));
29332953
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 1);
29342954
} else {
2935-
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 0);
2955+
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0);
2956+
if (_PyCompile_TopFBlock(c) || _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE ||
2957+
PyUnicode_READ_CHAR(alias->name, 0) == '*') {
2958+
// forced non-lazy import due to try/except or import *
2959+
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 2);
2960+
} else {
2961+
ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 0);
2962+
}
29362963
}
2964+
29372965
for (Py_ssize_t i = 0; i < n; i++) {
29382966
alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i);
29392967
identifier store_name;

0 commit comments

Comments
 (0)