Skip to content

Commit 1c691ea

Browse files
committed
Lazy imports grammar / AST changes
1 parent fb114cf commit 1c691ea

25 files changed

+1016
-338
lines changed

Grammar/python.gram

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ simple_stmts[asdl_stmt_seq*]:
121121
simple_stmt[stmt_ty] (memo):
122122
| assignment
123123
| &"type" type_alias
124+
| &('import' | 'from' | "lazy" ) import_stmt
124125
| e=star_expressions { _PyAST_Expr(e, EXTRA) }
125126
| &'return' return_stmt
126-
| &('import' | 'from') import_stmt
127127
| &'raise' raise_stmt
128128
| &'pass' pass_stmt
129129
| &'del' del_stmt
@@ -216,21 +216,24 @@ assert_stmt[stmt_ty]:
216216
| invalid_assert_stmt
217217
| 'assert' a=expression b=[',' z=expression { z }] { _PyAST_Assert(a, b, EXTRA) }
218218

219-
import_stmt[stmt_ty]:
219+
import_stmt[stmt_ty](memo):
220220
| invalid_import
221221
| import_name
222222
| import_from
223223

224224
# Import statements
225225
# -----------------
226226

227-
import_name[stmt_ty]: 'import' a=dotted_as_names { _PyAST_Import(a, EXTRA) }
227+
import_name[stmt_ty]:
228+
| 'import' a=dotted_as_names { _PyAST_Import(a, 0, EXTRA) }
229+
| "lazy" 'import' a=dotted_as_names { _PyAST_Import(a, 1, EXTRA) }
230+
228231
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
229232
import_from[stmt_ty]:
230-
| 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
231-
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), EXTRA) }
232-
| 'from' a=('.' | '...')+ 'import' b=import_from_targets {
233-
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), EXTRA) }
233+
| lazy="lazy"? 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
234+
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), lazy ? 1 : 0, EXTRA) }
235+
| lazy="lazy"? 'from' a=('.' | '...')+ 'import' b=import_from_targets {
236+
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), lazy ? 1 : 0, EXTRA) }
234237
import_from_targets[asdl_alias_seq*]:
235238
| '(' a=import_from_as_names [','] ')' { a }
236239
| import_from_as_names !','

Include/internal/pycore_ast.h

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

Include/internal/pycore_ast_state.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_ceval.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,11 @@ PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc,
297297
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
298298
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
299299
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
300-
PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *, _PyInterpreterFrame *, PyObject *, PyObject *, PyObject *);
300+
PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals,
301+
PyObject *name, PyObject *fromlist, PyObject *level);
302+
PyAPI_FUNC(PyObject *) _PyEval_LazyImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name);
303+
PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals,
304+
PyObject *name, PyObject *fromlist, PyObject *level);
301305
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
302306
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
303307
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);

Include/internal/pycore_import.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ extern int _PyImport_FixupBuiltin(
3131
PyObject *modules
3232
);
3333

34+
extern PyObject *
35+
_PyImport_ResolveName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
36+
extern PyObject *
37+
_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import);
38+
39+
3440
#ifdef HAVE_DLOPEN
3541
# include <dlfcn.h> // RTLD_NOW, RTLD_LAZY
3642
# if HAVE_DECL_RTLD_NOW
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* Copyright (c) Meta, Inc. and its affiliates. All Rights Reserved */
2+
/* File added for Lazy Imports */
3+
4+
/* Lazy object interface */
5+
6+
#ifndef Py_LAZYIMPORTOBJECT_H
7+
#define Py_LAZYIMPORTOBJECT_H
8+
#ifdef __cplusplus
9+
extern "C" {
10+
#endif
11+
12+
PyAPI_DATA(PyTypeObject) PyLazyImport_Type;
13+
#define PyLazyImport_CheckExact(op) Py_IS_TYPE((op), &PyLazyImport_Type)
14+
15+
typedef struct {
16+
PyObject_HEAD
17+
PyObject *lz_builtins;
18+
PyObject *lz_from;
19+
PyObject *lz_attr;
20+
} PyLazyImportObject;
21+
22+
23+
PyAPI_FUNC(PyObject *) _PyLazyImport_GetName(PyObject *lazy_import);
24+
PyAPI_FUNC(PyObject *) _PyLazyImport_New(PyObject *builtins, PyObject *from, PyObject *attr);
25+
26+
#ifdef __cplusplus
27+
}
28+
#endif
29+
#endif /* !Py_LAZYIMPORTOBJECT_H */

Include/internal/pycore_magic_number.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ Known values:
286286
Python 3.15a1 3653 (Fix handling of opcodes that may leave operands on the stack when optimizing LOAD_FAST)
287287
Python 3.15a1 3654 (Fix missing exception handlers in logical expression)
288288
Python 3.15a1 3655 (Fix miscompilation of some module-level annotations)
289+
Python 3.15a1 3656 Lazy imports IMPORT_NAME opcode changes
289290
290291
291292
Python 3.16 will start with 3700
@@ -299,7 +300,7 @@ PC/launcher.c must also be updated.
299300
300301
*/
301302

302-
#define PYC_MAGIC_NUMBER 3655
303+
#define PYC_MAGIC_NUMBER 3656
303304
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
304305
(little-endian) and then appending b'\r\n'. */
305306
#define PYC_MAGIC_NUMBER_TOKEN \

Lib/dis.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure', 'annotate')
3636

3737
ENTER_EXECUTOR = opmap['ENTER_EXECUTOR']
38+
IMPORT_NAME = opmap['IMPORT_NAME']
3839
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
3940
LOAD_SMALL_INT = opmap['LOAD_SMALL_INT']
4041
BINARY_OP = opmap['BINARY_OP']
@@ -601,6 +602,12 @@ def get_argval_argrepr(self, op, arg, offset):
601602
argval, argrepr = _get_name_info(arg//4, get_name)
602603
if (arg & 1) and argrepr:
603604
argrepr = f"{argrepr} + NULL|self"
605+
elif deop == IMPORT_NAME:
606+
argval, argrepr = _get_name_info(arg//4, get_name)
607+
if (arg & 1) and argrepr:
608+
argrepr = f"{argrepr} + lazy"
609+
elif (arg & 2) and argrepr:
610+
argrepr = f"{argrepr} + eager"
604611
else:
605612
argval, argrepr = _get_name_info(arg, get_name)
606613
elif deop in hasjump or deop in hasexc:

Lib/keyword.py

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

Lib/test/test_ast/data/ast_repr.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ Module(body=[Try(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='ex
6969
Module(body=[TryStar(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[Pass()], finalbody=[Pass()])], type_ignores=[])
7070
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=None)], type_ignores=[])
7171
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=Constant(value='message', kind=None))], type_ignores=[])
72-
Module(body=[Import(names=[alias(name='sys', asname=None)])], type_ignores=[])
73-
Module(body=[Import(names=[alias(name='foo', asname='bar')])], type_ignores=[])
74-
Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0)], type_ignores=[])
75-
Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0)], type_ignores=[])
72+
Module(body=[Import(names=[alias(name='sys', asname=None)], is_lazy=0)], type_ignores=[])
73+
Module(body=[Import(names=[alias(name='foo', asname='bar')], is_lazy=0)], type_ignores=[])
74+
Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0, is_lazy=0)], type_ignores=[])
75+
Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0, is_lazy=0)], type_ignores=[])
7676
Module(body=[Global(names=['v'])], type_ignores=[])
7777
Module(body=[Expr(value=Constant(value=1, kind=None))], type_ignores=[])
7878
Module(body=[Pass()], type_ignores=[])

0 commit comments

Comments
 (0)