From d48848c83e0f3e41b65c8f741f3fb6dbce5b9c29 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sun, 14 Mar 2021 18:01:30 +0000 Subject: bpo-39316: Make sure that attribute accesses and stores, including method calls, conform to PEP 626. (GH-24859) --- Lib/test/test_compile.py | 45 ++++++++++++++++++++++ .../2021-03-14-16-44-50.bpo-39316.Ns3a_F.rst | 3 ++ Python/compile.c | 18 ++++++++- Python/importlib.h | 6 +-- Python/importlib_external.h | 4 +- 5 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-03-14-16-44-50.bpo-39316.Ns3a_F.rst diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 1f12524..aa08c97 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -837,6 +837,51 @@ if 1: self.assertEqual(end, len(code.co_code)) self.assertEqual(line, code.co_firstlineno) + def test_lineno_attribute(self): + def load_attr(): + return ( + o. + a + ) + load_attr_lines = [ 2, 3, 1 ] + + def load_method(): + return ( + o. + m( + 0 + ) + ) + load_method_lines = [ 2, 3, 4, 3, 1 ] + + def store_attr(): + ( + o. + a + ) = ( + v + ) + store_attr_lines = [ 5, 2, 3 ] + + def aug_store_attr(): + ( + o. + a + ) += ( + v + ) + aug_store_attr_lines = [ 2, 3, 5, 1, 3 ] + + funcs = [ load_attr, load_method, store_attr, aug_store_attr] + func_lines = [ load_attr_lines, load_method_lines, + store_attr_lines, aug_store_attr_lines] + + for func, lines in zip(funcs, func_lines, strict=True): + with self.subTest(func=func): + code_lines = [ line-func.__code__.co_firstlineno + for (_, _, line) in func.__code__.co_lines() ] + self.assertEqual(lines, code_lines) + def test_big_dict_literal(self): # The compiler has a flushing point in "compiler_dict" that calls compiles diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-03-14-16-44-50.bpo-39316.Ns3a_F.rst b/Misc/NEWS.d/next/Core and Builtins/2021-03-14-16-44-50.bpo-39316.Ns3a_F.rst new file mode 100644 index 0000000..bebd9e8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-03-14-16-44-50.bpo-39316.Ns3a_F.rst @@ -0,0 +1,3 @@ +Tracing now has correct line numbers for attribute accesses when the +the attribute is on a different line from the object. +Improves debugging and profiling for multi-line method chains. diff --git a/Python/compile.c b/Python/compile.c index a841288..942614f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -4145,9 +4145,12 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e) /* Alright, we can optimize the code. */ VISIT(c, expr, meth->v.Attribute.value); + int old_lineno = c->u->u_lineno; + c->u->u_lineno = meth->end_lineno; ADDOP_NAME(c, LOAD_METHOD, meth->v.Attribute.attr, names); VISIT_SEQ(c, expr, e->v.Call.args); ADDOP_I(c, CALL_METHOD, asdl_seq_LEN(e->v.Call.args)); + c->u->u_lineno = old_lineno; return 1; } @@ -5113,12 +5116,21 @@ compiler_visit_expr1(struct compiler *c, expr_ty e) VISIT(c, expr, e->v.Attribute.value); switch (e->v.Attribute.ctx) { case Load: + { + int old_lineno = c->u->u_lineno; + c->u->u_lineno = e->end_lineno; ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names); + c->u->u_lineno = old_lineno; break; + } case Store: - if (forbidden_name(c, e->v.Attribute.attr, e->v.Attribute.ctx)) + if (forbidden_name(c, e->v.Attribute.attr, e->v.Attribute.ctx)) { return 0; + } + int old_lineno = c->u->u_lineno; + c->u->u_lineno = e->end_lineno; ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names); + c->u->u_lineno = old_lineno; break; case Del: ADDOP_NAME(c, DELETE_ATTR, e->v.Attribute.attr, names); @@ -5182,7 +5194,10 @@ compiler_augassign(struct compiler *c, stmt_ty s) case Attribute_kind: VISIT(c, expr, e->v.Attribute.value); ADDOP(c, DUP_TOP); + int old_lineno = c->u->u_lineno; + c->u->u_lineno = e->end_lineno; ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names); + c->u->u_lineno = old_lineno; break; case Subscript_kind: VISIT(c, expr, e->v.Subscript.value); @@ -5211,6 +5226,7 @@ compiler_augassign(struct compiler *c, stmt_ty s) switch (e->kind) { case Attribute_kind: + c->u->u_lineno = e->end_lineno; ADDOP(c, ROT_TWO); ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names); break; diff --git a/Python/importlib.h b/Python/importlib.h index 880343b..90cfa4c 100644 --- a/Python/importlib.h +++ b/Python/importlib.h @@ -694,7 +694,7 @@ const unsigned char _Py_M__importlib_bootstrap[] = { 218,4,106,111,105,110,41,2,114,33,0,0,0,114,62,0, 0,0,114,5,0,0,0,114,5,0,0,0,114,6,0,0, 0,114,53,0,0,0,123,1,0,0,115,22,0,0,0,10, - 1,10,1,4,255,10,2,18,1,10,1,8,1,4,1,6, + 1,10,1,4,255,10,2,18,1,10,1,6,1,8,1,4, 255,22,2,255,128,122,19,77,111,100,117,108,101,83,112,101, 99,46,95,95,114,101,112,114,95,95,99,2,0,0,0,0, 0,0,0,0,0,0,0,3,0,0,0,8,0,0,0,67, @@ -1552,8 +1552,8 @@ const unsigned char _Py_M__importlib_bootstrap[] = { 0,114,82,0,0,0,114,5,0,0,0,114,5,0,0,0, 114,6,0,0,0,218,14,95,102,105,110,100,95,97,110,100, 95,108,111,97,100,253,3,0,0,115,28,0,0,0,10,2, - 14,1,8,1,24,1,14,255,16,128,8,3,4,1,2,1, - 4,255,12,2,8,2,4,1,255,128,114,227,0,0,0,114, + 14,1,8,1,24,1,14,255,16,128,8,3,2,1,6,1, + 2,255,12,2,8,2,4,1,255,128,114,227,0,0,0,114, 25,0,0,0,99,3,0,0,0,0,0,0,0,0,0,0, 0,3,0,0,0,4,0,0,0,67,0,0,0,115,42,0, 0,0,116,0,124,0,124,1,124,2,131,3,1,0,124,2, diff --git a/Python/importlib_external.h b/Python/importlib_external.h index 108f59e..f904589 100644 --- a/Python/importlib_external.h +++ b/Python/importlib_external.h @@ -1173,8 +1173,8 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 3,114,130,0,0,0,218,6,109,111,100,117,108,101,114,175, 0,0,0,114,7,0,0,0,114,7,0,0,0,114,8,0, 0,0,218,11,101,120,101,99,95,109,111,100,117,108,101,60, - 3,0,0,115,14,0,0,0,12,2,8,1,6,1,4,1, - 6,255,20,2,255,128,122,25,95,76,111,97,100,101,114,66, + 3,0,0,115,14,0,0,0,12,2,8,1,4,1,8,1, + 4,255,20,2,255,128,122,25,95,76,111,97,100,101,114,66, 97,115,105,99,115,46,101,120,101,99,95,109,111,100,117,108, 101,99,2,0,0,0,0,0,0,0,0,0,0,0,2,0, 0,0,4,0,0,0,67,0,0,0,115,12,0,0,0,116, -- cgit v0.12