Skip to content

Commit 18b356b

Browse files
committed
gh-152631: Fix ast.parse on lazy imports with older feature_version
1 parent 55a09ed commit 18b356b

4 files changed

Lines changed: 406 additions & 256 deletions

File tree

‎Grammar/python.gram‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,24 @@ import_stmt[stmt_ty](memo):
225225
# -----------------
226226

227227
import_name[stmt_ty]:
228-
| lazy="lazy"? 'import' a=dotted_as_names { _PyAST_Import(a, lazy ? 1 : 0, EXTRA) }
228+
| "lazy" 'import' a=dotted_as_names {
229+
CHECK_VERSION(stmt_ty, 15, "Lazy imports are",
230+
_PyAST_Import(a, 1, EXTRA)) }
231+
| 'import' a=dotted_as_names { _PyAST_Import(a, 0, EXTRA) }
229232

230233
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
231234
import_from[stmt_ty]:
232-
| lazy="lazy"? 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
233-
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), lazy, EXTRA) }
234-
| lazy="lazy"? 'from' a=('.' | '...')+ 'import' b=import_from_targets {
235-
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), lazy ? 1 : 0, EXTRA) }
235+
| lazy="lazy" 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
236+
CHECK_VERSION(stmt_ty, 15, "Lazy imports are",
237+
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), lazy, EXTRA)) }
238+
| "lazy" 'from' a=('.' | '...')+ 'import' b=import_from_targets {
239+
CHECK_VERSION(stmt_ty, 15, "Lazy imports are",
240+
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), 1, EXTRA)) }
241+
| 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
242+
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), 0, EXTRA) }
243+
| 'from' a=('.' | '...')+ 'import' b=import_from_targets {
244+
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), 0, EXTRA) }
245+
236246
import_from_targets[asdl_alias_seq*]:
237247
| '(' a=import_from_as_names [','] ')' { a }
238248
| import_from_as_names !','

‎Lib/test/test_ast/test_ast.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,19 @@ def test_type_params_default_feature_version(self):
968968
with self.assertRaises(SyntaxError):
969969
ast.parse(sample, feature_version=(3, 12))
970970

971+
def test_lazy_imports_feature_version(self):
972+
samples = [
973+
"lazy import os",
974+
"lazy from .. import name",
975+
"lazy from mod import some",
976+
]
977+
for sample in samples:
978+
with self.subTest(sample):
979+
ast.parse(sample)
980+
ast.parse(sample, feature_version=(3, 15))
981+
with self.assertRaises(SyntaxError):
982+
ast.parse(sample, feature_version=(3, 14))
983+
971984
def test_invalid_major_feature_version(self):
972985
with self.assertRaises(ValueError):
973986
ast.parse('pass', feature_version=(2, 7))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`ast.parse` ability to parse lazy imports with *feature_version*
2+
older than 3.15, now it properly raises :exc:`SyntaxError`.

0 commit comments

Comments
 (0)