Skip to content

Commit ec981d6

Browse files
committed
[3.15] gh-152631: Fix ast.parse on lazy imports with older feature_version (GH-152632)
(cherry picked from commit 7b12a41) Co-authored-by: sobolevn <mail@sobolevn.me>
1 parent 99417e5 commit ec981d6

4 files changed

Lines changed: 408 additions & 258 deletions

File tree

‎Grammar/python.gram‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,25 @@ 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]:
232235
| invalid_import_from
233-
| lazy="lazy"? 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
234-
_PyPegen_checked_from_import(p, a, b, c, lazy, 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) }
236+
| lazy="lazy" 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
237+
CHECK_VERSION(stmt_ty, 15, "Lazy imports are",
238+
_PyPegen_checked_from_import(p, a, b, c, lazy, EXTRA)) }
239+
| "lazy" 'from' a=('.' | '...')+ 'import' b=import_from_targets {
240+
CHECK_VERSION(stmt_ty, 15, "Lazy imports are",
241+
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), 1, EXTRA)) }
242+
| 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
243+
_PyPegen_checked_from_import(p, a, b, c, NULL, EXTRA) }
244+
| 'from' a=('.' | '...')+ 'import' b=import_from_targets {
245+
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), 0, EXTRA) }
246+
237247
import_from_targets[asdl_alias_seq*]:
238248
| '(' a=import_from_as_names [','] ')' { a }
239249
| 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
@@ -973,6 +973,19 @@ def test_type_params_default_feature_version(self):
973973
with self.assertRaises(SyntaxError):
974974
ast.parse(sample, feature_version=(3, 12))
975975

976+
def test_lazy_imports_feature_version(self):
977+
samples = [
978+
"lazy import os",
979+
"lazy from .. import name",
980+
"lazy from mod import some",
981+
]
982+
for sample in samples:
983+
with self.subTest(sample):
984+
ast.parse(sample)
985+
ast.parse(sample, feature_version=(3, 15))
986+
with self.assertRaises(SyntaxError):
987+
ast.parse(sample, feature_version=(3, 14))
988+
976989
def test_invalid_major_feature_version(self):
977990
with self.assertRaises(ValueError):
978991
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)