Skip to content

Commit 3b0d745

Browse files
committed
Add __lazy_import__, check sys.modules before import
1 parent 1c691ea commit 3b0d745

18 files changed

+271
-39
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *c
298298
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
299299
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
300300
PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals,
301-
PyObject *name, PyObject *fromlist, PyObject *level);
301+
PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level);
302302
PyAPI_FUNC(PyObject *) _PyEval_LazyImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name);
303303
PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals,
304304
PyObject *name, PyObject *fromlist, PyObject *level);

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ struct _Py_global_strings {
155155
STRUCT_FOR_ID(__iter__)
156156
STRUCT_FOR_ID(__itruediv__)
157157
STRUCT_FOR_ID(__ixor__)
158+
STRUCT_FOR_ID(__lazy_import__)
158159
STRUCT_FOR_ID(__le__)
159160
STRUCT_FOR_ID(__len__)
160161
STRUCT_FOR_ID(__length_hint__)

Include/internal/pycore_import.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ extern PyObject *
3535
_PyImport_ResolveName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
3636
extern PyObject *
3737
_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import);
38+
extern PyObject *
39+
_PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *name, PyObject *builtins, PyObject *globals,
40+
PyObject *locals, PyObject *fromlist,
41+
int level);
3842

3943

4044
#ifdef HAVE_DLOPEN
@@ -79,6 +83,10 @@ extern int _PyImport_IsDefaultImportFunc(
7983
PyInterpreterState *interp,
8084
PyObject *func);
8185

86+
extern int _PyImport_IsDefaultLazyImportFunc(
87+
PyInterpreterState *interp,
88+
PyObject *func);
89+
8290
extern PyObject * _PyImport_GetImportlibLoader(
8391
PyInterpreterState *interp,
8492
const char *loader_name);

Include/internal/pycore_interp_structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ struct _import_state {
313313
int dlopenflags;
314314
#endif
315315
PyObject *import_func;
316+
PyObject *lazy_import_func;
316317
/* The global import lock. */
317318
_PyRecursiveMutex lock;
318319
/* diagnostic info in PyImport_ImportModuleLevelObject() */

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_import/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,31 @@ def test_disallowed_reimport(self):
25402540
self.assertIsNot(excsnap, None)
25412541

25422542

2543+
class LazyImportTests(unittest.TestCase):
2544+
def tearDown(self):
2545+
"""Make sure no modules pre-exist in sys.modules which are being used to
2546+
test."""
2547+
for key in list(sys.modules.keys()):
2548+
if key.startswith('test.test_import.data.lazy_imports'):
2549+
del sys.modules[key]
2550+
2551+
def test_basic_unused(self):
2552+
try:
2553+
import test.test_import.data.lazy_imports.basic_unused
2554+
except ImportError as e:
2555+
self.fail('lazy import failed')
2556+
2557+
self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules)
2558+
2559+
def test_basic_used(self):
2560+
try:
2561+
import test.test_import.data.lazy_imports.basic_used
2562+
except ImportError as e:
2563+
self.fail('lazy import failed')
2564+
2565+
self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules)
2566+
2567+
25432568
class TestSinglePhaseSnapshot(ModuleSnapshot):
25442569
"""A representation of a single-phase init module for testing.
25452570
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def f():
2+
pass
3+
4+
x = 42
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lazy import test.test_import.data.lazy_imports.basic2

0 commit comments

Comments
 (0)