summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2018-09-03 13:08:16 +1000
committerDamien George <damien.p.george@gmail.com>2018-09-03 13:08:16 +1000
commitb735208403a54774f9fd3d966f7c1a194c41870f (patch)
treebc91197e5af64043ad24e7d1de8373b304c0e637 /tests
parent828f771e327b932afc4865dbec53ce567dce45f5 (diff)
py/vm: Fix handling of finally-return with complex nested finallys.
Back in 8047340d7532ec32bc9f2d603bffc0bc9544297f basic support was added in the VM to handle return statements within a finally block. But it didn't cover all cases, in particular when some finally's were active and others inactive when the "return" was executed. This patch adds further support for return-within-finally by correctly managing the currently_in_except_block flag, and should fix all cases. The main point is that finally handlers remain on the exception stack even if they are active (currently being executed), and the unwind return code should only execute those finally's which are inactive. New tests are added for the cases which now pass.
Diffstat (limited to 'tests')
-rw-r--r--tests/basics/try_finally_return3.py103
-rwxr-xr-xtests/run-tests1
2 files changed, 104 insertions, 0 deletions
diff --git a/tests/basics/try_finally_return3.py b/tests/basics/try_finally_return3.py
new file mode 100644
index 000000000..a2a06ee97
--- /dev/null
+++ b/tests/basics/try_finally_return3.py
@@ -0,0 +1,103 @@
+# test 'return' within the finally block, with nested finally's
+# only inactive finally's should be executed, and only once
+
+# basic nested finally's, the print should only be executed once
+def f():
+ try:
+ raise TypeError
+ finally:
+ print(1)
+ try:
+ raise ValueError
+ finally:
+ return 42
+print(f())
+
+# similar to above but more nesting
+def f():
+ try:
+ raise ValueError
+ finally:
+ print(1)
+ try:
+ raise TypeError
+ finally:
+ print(2)
+ try:
+ pass
+ finally:
+ return 42
+print(f())
+
+# similar to above but even more nesting
+def f():
+ try:
+ raise ValueError
+ finally:
+ print(1)
+ try:
+ raise TypeError
+ finally:
+ print(2)
+ try:
+ raise Exception
+ finally:
+ print(3)
+ return 42
+print(f())
+
+# upon return some try's are active, some finally's are active, some inactive
+def f():
+ try:
+ try:
+ pass
+ finally:
+ print(2)
+ return 42
+ finally:
+ print(1)
+print(f())
+
+# same as above but raise instead of pass
+def f():
+ try:
+ try:
+ raise ValueError
+ finally:
+ print(2)
+ return 42
+ finally:
+ print(1)
+print(f())
+
+# upon return exception stack holds: active finally, inactive finally, active finally
+def f():
+ try:
+ raise Exception
+ finally:
+ print(1)
+ try:
+ try:
+ pass
+ finally:
+ print(3)
+ return 42
+ finally:
+ print(2)
+print(f())
+
+# same as above but raise instead of pass in innermost try block
+def f():
+ try:
+ raise Exception
+ finally:
+ print(1)
+ try:
+ try:
+ raise Exception
+ finally:
+ print(3)
+ return 42
+ finally:
+ print(2)
+print(f())
diff --git a/tests/run-tests b/tests/run-tests
index a3263fff8..cfbd01777 100755
--- a/tests/run-tests
+++ b/tests/run-tests
@@ -368,6 +368,7 @@ def run_tests(pyb, tests, args, base_path="."):
skip_tests.add('basics/try_finally_loops.py') # requires proper try finally code
skip_tests.add('basics/try_finally_return.py') # requires proper try finally code
skip_tests.add('basics/try_finally_return2.py') # requires proper try finally code
+ skip_tests.add('basics/try_finally_return3.py') # requires proper try finally code
skip_tests.add('basics/unboundlocal.py') # requires checking for unbound local
skip_tests.add('import/gen_context.py') # requires yield_value
skip_tests.add('misc/features.py') # requires raise_varargs