Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-43798: Add source location attributes to alias. #25324

Merged
merged 4 commits into from
Apr 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,20 @@ import_from[stmt_ty]:
import_from_targets[asdl_alias_seq*]:
| '(' a=import_from_as_names [','] ')' { a }
| import_from_as_names !','
| '*' { (asdl_alias_seq*)_PyPegen_singleton_seq(p, CHECK(alias_ty, _PyPegen_alias_for_star(p))) }
| '*' { (asdl_alias_seq*)_PyPegen_singleton_seq(p, CHECK(alias_ty, _PyPegen_alias_for_star(p, EXTRA))) }
| invalid_import_from_targets
import_from_as_names[asdl_alias_seq*]:
| a[asdl_alias_seq*]=','.import_from_as_name+ { a }
import_from_as_name[alias_ty]:
| a=NAME b=['as' z=NAME { z }] { _PyAST_alias(a->v.Name.id,
(b) ? ((expr_ty) b)->v.Name.id : NULL,
p->arena) }
EXTRA) }
dotted_as_names[asdl_alias_seq*]:
| a[asdl_alias_seq*]=','.dotted_as_name+ { a }
dotted_as_name[alias_ty]:
| a=dotted_name b=['as' z=NAME { z }] { _PyAST_alias(a->v.Name.id,
(b) ? ((expr_ty) b)->v.Name.id : NULL,
p->arena) }
EXTRA) }
dotted_name[expr_ty]:
| a=dotted_name '.' b=NAME { _PyPegen_join_names_with_dot(p, a, b) }
| NAME
Expand Down
8 changes: 7 additions & 1 deletion Include/internal/pycore_ast.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Lib/test/test_asdl_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ def test_product(self):
alias = self.types['alias']
self.assertEqual(
str(alias),
'Product([Field(identifier, name), Field(identifier, asname, opt=True)])')
'Product([Field(identifier, name), Field(identifier, asname, opt=True)], '
'[Field(int, lineno), Field(int, col_offset), '
'Field(int, end_lineno, opt=True), Field(int, end_col_offset, opt=True)])')

def test_attributes(self):
stmt = self.types['stmt']
Expand Down
28 changes: 25 additions & 3 deletions Lib/test/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,26 @@ def test_non_interned_future_from_ast(self):
mod.body[0].module = " __future__ ".strip()
compile(mod, "<test>", "exec")

def test_alias(self):
im = ast.parse("from bar import y").body[0]
self.assertEqual(len(im.names), 1)
alias = im.names[0]
self.assertEqual(alias.name, 'y')
self.assertIsNone(alias.asname)
self.assertEqual(alias.lineno, 1)
self.assertEqual(alias.end_lineno, 1)
self.assertEqual(alias.col_offset, 16)
self.assertEqual(alias.end_col_offset, 17)

im = ast.parse("from bar import *").body[0]
alias = im.names[0]
self.assertEqual(alias.name, '*')
self.assertIsNone(alias.asname)
self.assertEqual(alias.lineno, 1)
self.assertEqual(alias.end_lineno, 1)
self.assertEqual(alias.col_offset, 16)
self.assertEqual(alias.end_col_offset, 17)

def test_base_classes(self):
self.assertTrue(issubclass(ast.For, ast.stmt))
self.assertTrue(issubclass(ast.Name, ast.expr))
Expand Down Expand Up @@ -1037,7 +1057,8 @@ def test_bad_integer(self):

def test_level_as_none(self):
body = [ast.ImportFrom(module='time',
names=[ast.alias(name='sleep')],
names=[ast.alias(name='sleep',
lineno=0, col_offset=0)],
level=None,
lineno=0, col_offset=0)]
mod = ast.Module(body, [])
Expand Down Expand Up @@ -1735,6 +1756,7 @@ def test_import_from_multi_line(self):
''').strip()
imp = ast.parse(s).body[0]
self._check_end_pos(imp, 3, 1)
self._check_end_pos(imp.names[2], 2, 16)

def test_slices(self):
s1 = 'f()[1, 2] [0]'
Expand Down Expand Up @@ -2095,8 +2117,8 @@ def main():
('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [], [], [('Pass', (4, 2, 4, 6))])], []),
('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []),
('Module', [('Import', (1, 0, 1, 10), [('alias', 'sys', None)])], []),
('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', 'v', None)], 0)], []),
('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)])], []),
('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0)], []),
('Module', [('Global', (1, 0, 1, 8), ['v'])], []),
('Module', [('Expr', (1, 0, 1, 1), ('Constant', (1, 0, 1, 1), 1, None))], []),
('Module', [('Pass', (1, 0, 1, 4))], []),
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_peg_generator/test_c_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def test_same_name_different_types(self) -> None:
)
simple_name[expr_ty]: NAME
import_as_names_from[asdl_alias_seq*]: a[asdl_alias_seq*]=','.import_as_name_from+ { a }
import_as_name_from[alias_ty]: a=NAME 'as' b=NAME { _PyAST_alias(((expr_ty) a)->v.Name.id, ((expr_ty) b)->v.Name.id, p->arena) }
import_as_name_from[alias_ty]: a=NAME 'as' b=NAME { _PyAST_alias(((expr_ty) a)->v.Name.id, ((expr_ty) b)->v.Name.id, EXTRA) }
"""
test_source = """
for stmt in ("from a import b as c", "from . import a as b"):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:class:`ast.alias` nodes now include source location metadata attributes e.g. lineno, col_offset.
1 change: 1 addition & 0 deletions Parser/Python.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ module Python

-- import name with optional 'as' alias.
alias = (identifier name, identifier? asname)
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

withitem = (expr context_expr, expr? optional_vars)

Expand Down
60 changes: 57 additions & 3 deletions Parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -3310,6 +3310,15 @@ import_from_targets_rule(Parser *p)
}
asdl_alias_seq* _res = NULL;
int _mark = p->mark;
if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
p->error_indicator = 1;
D(p->level--);
return NULL;
}
int _start_lineno = p->tokens[_mark]->lineno;
UNUSED(_start_lineno); // Only used by EXTRA macro
int _start_col_offset = p->tokens[_mark]->col_offset;
UNUSED(_start_col_offset); // Only used by EXTRA macro
{ // '(' import_from_as_names ','? ')'
if (p->error_indicator) {
D(p->level--);
Expand Down Expand Up @@ -3377,7 +3386,16 @@ import_from_targets_rule(Parser *p)
)
{
D(fprintf(stderr, "%*c+ import_from_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'"));
_res = ( asdl_alias_seq * ) _PyPegen_singleton_seq ( p , CHECK ( alias_ty , _PyPegen_alias_for_star ( p ) ) );
Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
if (_token == NULL) {
D(p->level--);
return NULL;
}
int _end_lineno = _token->end_lineno;
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 = ( asdl_alias_seq * ) _PyPegen_singleton_seq ( p , CHECK ( alias_ty , _PyPegen_alias_for_star ( p , EXTRA ) ) );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
D(p->level--);
Expand Down Expand Up @@ -3466,6 +3484,15 @@ import_from_as_name_rule(Parser *p)
}
alias_ty _res = NULL;
int _mark = p->mark;
if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
p->error_indicator = 1;
D(p->level--);
return NULL;
}
int _start_lineno = p->tokens[_mark]->lineno;
UNUSED(_start_lineno); // Only used by EXTRA macro
int _start_col_offset = p->tokens[_mark]->col_offset;
UNUSED(_start_col_offset); // Only used by EXTRA macro
{ // NAME ['as' NAME]
if (p->error_indicator) {
D(p->level--);
Expand All @@ -3481,7 +3508,16 @@ import_from_as_name_rule(Parser *p)
)
{
D(fprintf(stderr, "%*c+ import_from_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME ['as' NAME]"));
_res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , p -> arena );
Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
if (_token == NULL) {
D(p->level--);
return NULL;
}
int _end_lineno = _token->end_lineno;
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_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , EXTRA );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
D(p->level--);
Expand Down Expand Up @@ -3551,6 +3587,15 @@ dotted_as_name_rule(Parser *p)
}
alias_ty _res = NULL;
int _mark = p->mark;
if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
p->error_indicator = 1;
D(p->level--);
return NULL;
}
int _start_lineno = p->tokens[_mark]->lineno;
UNUSED(_start_lineno); // Only used by EXTRA macro
int _start_col_offset = p->tokens[_mark]->col_offset;
UNUSED(_start_col_offset); // Only used by EXTRA macro
{ // dotted_name ['as' NAME]
if (p->error_indicator) {
D(p->level--);
Expand All @@ -3566,7 +3611,16 @@ dotted_as_name_rule(Parser *p)
)
{
D(fprintf(stderr, "%*c+ dotted_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name ['as' NAME]"));
_res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , p -> arena );
Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
if (_token == NULL) {
D(p->level--);
return NULL;
}
int _end_lineno = _token->end_lineno;
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_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , EXTRA );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
D(p->level--);
Expand Down
6 changes: 3 additions & 3 deletions Parser/pegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1555,8 +1555,8 @@ _PyPegen_seq_count_dots(asdl_seq *seq)

/* Creates an alias with '*' as the identifier name */
alias_ty
_PyPegen_alias_for_star(Parser *p)
{
_PyPegen_alias_for_star(Parser *p, int lineno, int col_offset, int end_lineno,
int end_col_offset, PyArena *arena) {
PyObject *str = PyUnicode_InternFromString("*");
if (!str) {
return NULL;
Expand All @@ -1565,7 +1565,7 @@ _PyPegen_alias_for_star(Parser *p)
Py_DECREF(str);
return NULL;
}
return _PyAST_alias(str, NULL, p->arena);
return _PyAST_alias(str, NULL, lineno, col_offset, end_lineno, end_col_offset, arena);
}

/* Creates a new asdl_seq* with the identifiers of all the names in seq */
Expand Down
2 changes: 1 addition & 1 deletion Parser/pegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ asdl_seq *_PyPegen_seq_append_to_end(Parser *, asdl_seq *, void *);
asdl_seq *_PyPegen_seq_flatten(Parser *, asdl_seq *);
expr_ty _PyPegen_join_names_with_dot(Parser *, expr_ty, expr_ty);
int _PyPegen_seq_count_dots(asdl_seq *);
alias_ty _PyPegen_alias_for_star(Parser *);
alias_ty _PyPegen_alias_for_star(Parser *, int, int, int, int, PyArena *);
asdl_identifier_seq *_PyPegen_map_names_to_ids(Parser *, asdl_expr_seq *);
CmpopExprPair *_PyPegen_cmpop_expr_pair(Parser *, cmpop_ty, expr_ty);
asdl_int_seq *_PyPegen_get_cmpops(Parser *p, asdl_seq *);
Expand Down
Loading