Skip to content

Commit 5274b68

Browse files
authored
bpo-42645: Make sure that return/break/continue are only traced once when exiting via a finally block. (GH-23780)
* Make sure that return/break/continue are only traced once when exiting via a finally block. * Add test for return in try-finally. * Update importlib
1 parent c71581c commit 5274b68

File tree

4 files changed

+1790
-1676
lines changed

4 files changed

+1790
-1676
lines changed

Lib/test/test_dis.py

+4-12
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,12 @@ def _tryfinallyconst(b):
366366
%3d 6 LOAD_FAST 1 (b)
367367
8 CALL_FUNCTION 0
368368
10 POP_TOP
369-
370-
%3d 12 RETURN_VALUE
371-
372-
%3d >> 14 LOAD_FAST 1 (b)
369+
12 RETURN_VALUE
370+
>> 14 LOAD_FAST 1 (b)
373371
16 CALL_FUNCTION 0
374372
18 POP_TOP
375373
20 RERAISE
376374
""" % (_tryfinally.__code__.co_firstlineno + 1,
377-
_tryfinally.__code__.co_firstlineno + 2,
378-
_tryfinally.__code__.co_firstlineno + 4,
379375
_tryfinally.__code__.co_firstlineno + 2,
380376
_tryfinally.__code__.co_firstlineno + 4,
381377
)
@@ -388,17 +384,13 @@ def _tryfinallyconst(b):
388384
%3d 4 LOAD_FAST 0 (b)
389385
6 CALL_FUNCTION 0
390386
8 POP_TOP
391-
392-
%3d 10 LOAD_CONST 1 (1)
387+
10 LOAD_CONST 1 (1)
393388
12 RETURN_VALUE
394-
395-
%3d >> 14 LOAD_FAST 0 (b)
389+
>> 14 LOAD_FAST 0 (b)
396390
16 CALL_FUNCTION 0
397391
18 POP_TOP
398392
20 RERAISE
399393
""" % (_tryfinallyconst.__code__.co_firstlineno + 1,
400-
_tryfinallyconst.__code__.co_firstlineno + 2,
401-
_tryfinallyconst.__code__.co_firstlineno + 4,
402394
_tryfinallyconst.__code__.co_firstlineno + 2,
403395
_tryfinallyconst.__code__.co_firstlineno + 4,
404396
)

Lib/test/test_sys_settrace.py

+113
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,119 @@ def func():
678678
(4, 'line'),
679679
(4, 'return')])
680680

681+
def test_if_break(self):
682+
683+
def func():
684+
seq = [1, 0]
685+
while seq:
686+
n = seq.pop()
687+
if n:
688+
break # line 5
689+
else:
690+
n = 99
691+
return n # line 8
692+
693+
self.run_and_compare(func,
694+
[(0, 'call'),
695+
(1, 'line'),
696+
(2, 'line'),
697+
(3, 'line'),
698+
(4, 'line'),
699+
(2, 'line'),
700+
(3, 'line'),
701+
(4, 'line'),
702+
(5, 'line'),
703+
(8, 'line'),
704+
(8, 'return')])
705+
706+
def test_break_through_finally(self):
707+
708+
def func():
709+
a, c, d, i = 1, 1, 1, 99
710+
try:
711+
for i in range(3):
712+
try:
713+
a = 5
714+
if i > 0:
715+
break # line 7
716+
a = 8
717+
finally:
718+
c = 10
719+
except:
720+
d = 12 # line 12
721+
assert a == 5 and c == 10 and d == 1 # line 13
722+
723+
self.run_and_compare(func,
724+
[(0, 'call'),
725+
(1, 'line'),
726+
(2, 'line'),
727+
(3, 'line'),
728+
(4, 'line'),
729+
(5, 'line'),
730+
(6, 'line'),
731+
(8, 'line'),
732+
(10, 'line'),
733+
(3, 'line'),
734+
(4, 'line'),
735+
(5, 'line'),
736+
(6, 'line'),
737+
(7, 'line'),
738+
(10, 'line'),
739+
(13, 'line'),
740+
(13, 'return')])
741+
742+
def test_continue_through_finally(self):
743+
744+
def func():
745+
a, b, c, d, i = 1, 1, 1, 1, 99
746+
try:
747+
for i in range(2):
748+
try:
749+
a = 5
750+
if i > 0:
751+
continue # line 7
752+
b = 8
753+
finally:
754+
c = 10
755+
except:
756+
d = 12 # line 12
757+
assert (a, b, c, d) == (5, 8, 10, 1) # line 13
758+
759+
self.run_and_compare(func,
760+
[(0, 'call'),
761+
(1, 'line'),
762+
(2, 'line'),
763+
(3, 'line'),
764+
(4, 'line'),
765+
(5, 'line'),
766+
(6, 'line'),
767+
(8, 'line'),
768+
(10, 'line'),
769+
(3, 'line'),
770+
(4, 'line'),
771+
(5, 'line'),
772+
(6, 'line'),
773+
(7, 'line'),
774+
(10, 'line'),
775+
(3, 'line'),
776+
(13, 'line'),
777+
(13, 'return')])
778+
779+
def test_return_through_finally(self):
780+
781+
def func():
782+
try:
783+
return 2
784+
finally:
785+
4
786+
787+
self.run_and_compare(func,
788+
[(0, 'call'),
789+
(1, 'line'),
790+
(2, 'line'),
791+
(4, 'line'),
792+
(4, 'return')])
793+
681794

682795
class SkipLineEventsTraceTestCase(TraceTestCase):
683796
"""Repeat the trace tests, but with per-line events skipped"""

Python/compile.c

+13-4
Original file line numberDiff line numberDiff line change
@@ -1695,19 +1695,22 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
16951695
return 1;
16961696

16971697
case FINALLY_TRY:
1698+
/* This POP_BLOCK gets the line number of the unwinding statement */
16981699
ADDOP(c, POP_BLOCK);
16991700
if (preserve_tos) {
17001701
if (!compiler_push_fblock(c, POP_VALUE, NULL, NULL, NULL)) {
17011702
return 0;
17021703
}
17031704
}
1704-
/* Emit the finally block, restoring the line number when done */
1705-
int saved_lineno = c->u->u_lineno;
1705+
/* Emit the finally block */
17061706
VISIT_SEQ(c, stmt, info->fb_datum);
1707-
c->u->u_lineno = saved_lineno;
17081707
if (preserve_tos) {
17091708
compiler_pop_fblock(c, POP_VALUE, NULL);
17101709
}
1710+
/* The finally block should appear to execute after the
1711+
* statement causing the unwinding, so make the unwinding
1712+
* instruction artificial */
1713+
c->u->u_lineno = -1;
17111714
return 1;
17121715

17131716
case FINALLY_END:
@@ -2859,14 +2862,20 @@ compiler_return(struct compiler *c, stmt_ty s)
28592862
}
28602863
if (preserve_tos) {
28612864
VISIT(c, expr, s->v.Return.value);
2865+
} else {
2866+
/* Emit instruction with line number for expression */
2867+
if (s->v.Return.value != NULL) {
2868+
SET_LOC(c, s->v.Return.value);
2869+
ADDOP(c, NOP);
2870+
}
28622871
}
28632872
if (!compiler_unwind_fblock_stack(c, preserve_tos, NULL))
28642873
return 0;
28652874
if (s->v.Return.value == NULL) {
28662875
ADDOP_LOAD_CONST(c, Py_None);
28672876
}
28682877
else if (!preserve_tos) {
2869-
VISIT(c, expr, s->v.Return.value);
2878+
ADDOP_LOAD_CONST(c, s->v.Return.value->v.Constant.value);
28702879
}
28712880
ADDOP(c, RETURN_VALUE);
28722881
NEXT_BLOCK(c);

0 commit comments

Comments
 (0)