-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-33211: Change line number of decorated nodes back to def #6460
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
Changes from 14 commits
b60b6a6
ae578b0
191bd38
da42832
649506c
29b0d60
68b2228
09d8ab2
df9fbe2
9840f84
0842c20
7f4fcdf
e4e7f1a
a8e747d
3df5862
44a3cd9
afd3b7e
bdc9942
914d0a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
import dis | ||
import os | ||
import sys | ||
import textwrap | ||
import unittest | ||
import weakref | ||
|
||
|
@@ -43,12 +44,36 @@ def to_tuple(t): | |
"def f(**kwargs): pass", | ||
# FunctionDef with all kind of args and docstring | ||
"def f(a, b=1, c=None, d=[], e={}, *args, f=42, **kwargs): 'doc for f()'", | ||
# Decorated FunctionDef | ||
textwrap.dedent(""" | ||
@deco | ||
def f():... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: add a space after |
||
"""), | ||
# Multiple decorators on a FunctionDef | ||
textwrap.dedent(""" | ||
@deco1 | ||
@deco2() | ||
def f():... | ||
"""), | ||
# ClassDef | ||
"class C:pass", | ||
# ClassDef with docstring | ||
"class C: 'docstring for class C'", | ||
# ClassDef, new style class | ||
"class C(object): pass", | ||
# Decorated ClassDef | ||
textwrap.dedent(""" | ||
@foo | ||
class A: | ||
pass | ||
"""), | ||
# Multiple decorators on a ClassDef | ||
textwrap.dedent(""" | ||
@foo1 | ||
@bar(4) | ||
class A: | ||
pass | ||
"""), | ||
# Return | ||
"def f():return 1", | ||
# Delete | ||
|
@@ -207,7 +232,9 @@ def _assertTrueorder(self, ast_node, parent_pos): | |
parent_pos = (ast_node.lineno, ast_node.col_offset) | ||
for name in ast_node._fields: | ||
value = getattr(ast_node, name) | ||
if isinstance(value, list): | ||
# Since decorators are stored as an attribute of a FunctionDef | ||
# they don't follow the true order of the syntax. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a test that all decorators are before a FunctionDef? |
||
if isinstance(value, list) and name != 'decorator_list': | ||
for child in value: | ||
self._assertTrueorder(child, parent_pos) | ||
elif value is not None: | ||
|
@@ -1133,9 +1160,13 @@ def main(): | |
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], ('arg', (1, 7), 'args', None), [], [], None, []), [('Pass', (1, 14))], [], None, None)], None), | ||
8000 ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], ('arg', (1, 8), 'kwargs', None), []), [('Pass', (1, 17))], [], None, None)], None), | ||
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None), ('arg', (1, 9), 'b', None), ('arg', (1, 14), 'c', None), ('arg', (1, 22), 'd', None), ('arg', (1, 28), 'e', None)], ('arg', (1, 35), 'args', None), [('arg', (1, 41), 'f', None)], [('Num', (1, 43), 42)], ('arg', (1, 49), 'kwargs', None), [('Num', (1, 11), 1), ('NameConstant', (1, 16), None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [], [], None, 'doc for f()')], None), | ||
('Module', [('FunctionDef', (3, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (3, 8), ('Ellipsis', (3, 8)))], [('Name', (2, 1), 'deco', ('Load',))], None, None)], None), | ||
('Module', [('FunctionDef', (4, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (4, 8), ('Ellipsis', (4, 8)))], [('Name', (2, 1), 'deco1', ('Load',)), ('Call', (3, 0), ('Name', (3, 1), 'deco2', ('Load',)), [], [])], None, None)], None), | ||
('Module', [('ClassDef', (1, 0), 'C', [], [], [('Pass', (1, 8))], [], None)], None), | ||
('Module', [('ClassDef', (1, 0), 'C', [], [], [], [], 'docstring for class C')], None), | ||
('Module', [('ClassDef', (1, 0), 'C', [('Name', (1, 8), 'object', ('Load',))], [], [('Pass', (1, 17))], [], None)], None), | ||
('Module', [('ClassDef', (3, 0), 'A', [], [], [('Pass', (4, 4))], [('Name', (2, 1), 'foo', ('Load',))], None)], None), | ||
('Module', [('ClassDef', (4, 0), 'A', [], [], [('Pass', (5, 4))], [('Name', (2, 1), 'foo1', ('Load',)), ('Call', (3, 1), ('Name', (3, 1), 'bar', ('Load',)), [('Num', (3, 5), 4)], [])], None)], None), | ||
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Return', (1, 8), ('Num', (1, 15), 1))], [], None, None)], None), | ||
('Module', [('Delete', (1, 0), [('Name', (1, 4), 'v', ('Del',))])], None), | ||
('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Num', (1, 4), 1))], None), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Change the line number of decorated defs back to the def or class token in the | ||
AST, not the first decorator. Have the change of line number happen in the | ||
compiler. | ||
Th 8000 ere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add "Patch by *yourname"." And add your name in Misc/ACKS. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1672,7 +1672,6 @@ static stmt_ty | |
ast_for_decorated(struct compiling *c, const node *n) | ||
{ | ||
/* decorated: decorators (classdef | funcdef | async_funcdef) */ | ||
stmt_ty thing = NULL; | ||
asdl_seq *decorator_seq = NULL; | ||
|
||
REQ(n, decorated); | ||
|
@@ -1686,19 +1685,14 @@ ast_for_decorated(struct compiling *c, const node *n) | |
TYPE(CHILD(n, 1)) == classdef); | ||
|
||
if (TYPE(CHILD(n, 1)) == funcdef) { | ||
thing = ast_for_funcdef(c, CHILD(n, 1), decorator_seq); | ||
return ast_for_funcdef(c, CHILD(n, 1), decorator_seq); | ||
} else if (TYPE(CHILD(n, 1)) == classdef) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually now you can remove all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'd think so, sadly MSVC warns that not all code paths return if there isn't an else. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, with the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to remove other |
||
thing = ast_for_classdef(c, CHILD(n, 1), decorator_seq); | ||
return ast_for_classdef(c, CHILD(n, 1), decorator_seq); | ||
} else if (TYPE(CHILD(n, 1)) == async_funcdef) { | ||
thing = ast_for_async_funcdef(c, CHILD(n, 1), decorator_seq); | ||
} | ||
/* we count the decorators in when talking about the class' or | ||
* function's line number */ | ||
if (thing) { | ||
thing->lineno = LINENO(n); | ||
thing->col_offset = n->n_col_offset; | ||
return ast_for_async_funcdef(c, CHILD(n, 1), decorator_seq); | ||
} else { | ||
Py_UNREACHABLE(); | ||
} | ||
return thing; | ||
} | ||
|
||
static expr_ty | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1949,6 +1949,26 @@ compiler_default_arguments(struct compiler *c, arguments_ty args) | |
return funcflags; | ||
} | ||
|
||
static int corrected_firstlineno(struct compiler *c, stmt_ty s, | ||
asdl_seq * decos) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why weird indentation on a separate line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think patchcheck messed up some of this up, sorry for the noise. On the |
||
{ | ||
/* To keep the ability to get the relevant source of a decorated item | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Us 4 spaces for indentation. |
||
using inspect.getsource, we need to keep the first line number | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Align wrapped lines to "To keep", not to "/*". |
||
of the code object as the first line number of the first decorator. | ||
This used to be done via the AST, but it is better to hide this | ||
internally. | ||
*/ | ||
if (asdl_seq_LEN(decos) > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
expr_ty first_decorator = asdl_seq_GET(decos, 0); | ||
c->u->u_firstlineno = first_decorator->lineno; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems Add a regression test for these cases: def f():
@deco
def g(): pass class A:
@deco
def m(self): pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm probably missing something, but the generated AST is:
which seems correct. Perhaps I am misunderstanding? |
||
return first_decorator->lineno; | ||
} | ||
else { | ||
return s->lineno; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This empty line is unnecessary. |
||
} | ||
|
||
static int | ||
compiler_function(struct compiler *c, stmt_ty s, int is_async) | ||
{ | ||
|
@@ -1988,6 +2008,10 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async) | |
if (!compiler_decorators(c, decos)) | ||
return 0; | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Too many empty lines. |
||
int first_lineno = corrected_firstlineno(c, s, decos); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation is misleading, shift left. |
||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Too many empty lines |
||
funcflags = compiler_default_arguments(c, args); | ||
if (funcflags == -1) { | ||
return 0; | ||
|
@@ -2001,7 +2025,7 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async) | |
funcflags |= 0x04; | ||
} | ||
|
||
if (!compiler_enter_scope(c, name, scope_type, (void *)s, s->lineno)) { | ||
if (!compiler_enter_scope(c, name, scope_type, (void *)s, first_lineno)) { | ||
return 0; | ||
} | ||
|
||
|
@@ -2050,6 +2074,8 @@ compiler_class(struct compiler *c, stmt_ty s) | |
if (!compiler_decorators(c, decos)) | ||
return 0; | ||
|
||
int first_lineno = corrected_firstlineno(c, s, decos); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong indentation. |
||
|
||
/* ultimately generate code for: | ||
<name> = __build_class__(<func>, <name>, *<bases>, **<keywords>) | ||
where: | ||
|
@@ -2064,7 +2090,7 @@ compiler_class(struct compiler *c, stmt_ty s) | |
|
||
/* 1. compile the class body into a code object */ | ||
if (!compiler_enter_scope(c, s->v.ClassDef.name, | ||
COMPILER_SCOPE_CLASS, (void *)s, s->lineno)) | ||
COMPILER_SCOPE_CLASS, (void *)s, first_lineno)) | ||
return 0; | ||
/* this block represents what we do in the new scope */ | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you use
textwrap.dedent()
it would clearer if add an indentation of the inner code.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please increase the indentation of these fragments.