[3.13] gh-125331: Allow the parser to activate future imports on the fly (GH-125482) (#131062)

gh-125331: Allow the parser to activate future imports on the fly (GH-125482)
(cherry picked from commit 3bd3e09588)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-03-10 23:20:38 +01:00 committed by GitHub
parent 977af3a6a2
commit ebd2ed7ad8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 2 deletions

View File

@ -207,7 +207,7 @@ import_name[stmt_ty]: 'import' a=dotted_as_names { _PyAST_Import(a, EXTRA) }
# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
import_from[stmt_ty]:
| 'from' a=('.' | '...')* b=dotted_name 'import' c=import_from_targets {
_PyAST_ImportFrom(b->v.Name.id, c, _PyPegen_seq_count_dots(a), EXTRA) }
_PyPegen_checked_future_import(p, b->v.Name.id, c, _PyPegen_seq_count_dots(a), EXTRA) }
| 'from' a=('.' | '...')+ 'import' b=import_from_targets {
_PyAST_ImportFrom(NULL, b, _PyPegen_seq_count_dots(a), EXTRA) }
import_from_targets[asdl_alias_seq*]:

View File

@ -34,6 +34,32 @@ def test_guido_as_bdfl(self):
# parser reports the start of the token
self.assertEqual(cm.exception.offset, 3)
def test_barry_as_bdfl_look_ma_with_no_compiler_flags(self):
# Check that the future import is handled by the parser
# even if the compiler flags are not passed.
code = "from __future__ import barry_as_FLUFL;2 {0} 3"
compile(code.format('<>'), '<BDFL test>', 'exec')
with self.assertRaises(SyntaxError) as cm:
compile(code.format('!='), '<FLUFL test>', 'exec')
self.assertRegex(str(cm.exception), "with Barry as BDFL, use '<>' instead of '!='")
self.assertIn('2 != 3', cm.exception.text)
self.assertEqual(cm.exception.filename, '<FLUFL test>')
self.assertEqual(cm.exception.lineno, 1)
self.assertEqual(cm.exception.offset, len(code) - 4)
def test_barry_as_bdfl_relative_import(self):
code = "from .__future__ import barry_as_FLUFL;2 {0} 3"
compile(code.format('!='), '<FLUFL test>', 'exec')
with self.assertRaises(SyntaxError) as cm:
compile(code.format('<>'), '<BDFL test>', 'exec')
self.assertRegex(str(cm.exception), "<BDFL test>")
self.assertIn('2 <> 3', cm.exception.text)
self.assertEqual(cm.exception.filename, '<BDFL test>')
self.assertEqual(cm.exception.lineno, 1)
self.assertEqual(cm.exception.offset, len(code) - 4)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,5 @@
``from __future__ import barry_as_FLUFL`` now works in more contexts,
including when it is used in files, with the ``-c`` flag, and in the REPL
when there are multiple statements on the same line. Previously, it worked
only on subsequent lines in the REPL, and when the appropriate flags were
passed directly to :func:`compile`. Patch by Pablo Galindo.

View File

@ -1685,3 +1685,18 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
assert(current_pos == n_elements);
return _PyAST_JoinedStr(values, lineno, col_offset, end_lineno, end_col_offset, p->arena);
}
stmt_ty
_PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * names, int level,
int lineno, int col_offset, int end_lineno, int end_col_offset,
PyArena *arena) {
if (level == 0 && PyUnicode_CompareWithASCIIString(module, "__future__") == 0) {
for (Py_ssize_t i = 0; i < asdl_seq_LEN(names); i++) {
alias_ty alias = asdl_seq_GET(names, i);
if (PyUnicode_CompareWithASCIIString(alias->name, "barry_as_FLUFL") == 0) {
p->flags |= PyPARSE_BARRY_AS_BDFL;
}
}
}
return _PyAST_ImportFrom(module, names, level, lineno, col_offset, end_lineno, end_col_offset, arena);
}

2
Parser/parser.c generated
View File

@ -3572,7 +3572,7 @@ import_from_rule(Parser *p)
UNUSED(_end_lineno); // Only used by EXTRA macro
int _end_col_offset = _token->end_col_offset;
UNUSED(_end_col_offset); // Only used by EXTRA macro
_res = _PyAST_ImportFrom ( b -> v . Name . id , c , _PyPegen_seq_count_dots ( a ) , EXTRA );
_res = _PyPegen_checked_future_import ( p , b -> v . Name . id , c , _PyPegen_seq_count_dots ( a ) , EXTRA );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
p->level--;

View File

@ -346,6 +346,8 @@ mod_ty _PyPegen_make_module(Parser *, asdl_stmt_seq *);
void *_PyPegen_arguments_parsing_error(Parser *, expr_ty);
expr_ty _PyPegen_get_last_comprehension_item(comprehension_ty comprehension);
void *_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args, asdl_comprehension_seq *comprehensions);
stmt_ty _PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq *,
int , int, int , int , int , PyArena *);
// Parser API