Async-aware try/finally handling for when finally contains await. This version uses a modified approach that preserves exceptions across await.
(
builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc, line: int = -1
)
| 927 | |
| 928 | |
| 929 | def transform_try_finally_stmt_async( |
| 930 | builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc, line: int = -1 |
| 931 | ) -> None: |
| 932 | """Async-aware try/finally handling for when finally contains await. |
| 933 | |
| 934 | This version uses a modified approach that preserves exceptions across await.""" |
| 935 | |
| 936 | # We need to handle returns properly, so we'll use TryFinallyNonlocalControl |
| 937 | # to track return values, similar to the regular try/finally implementation |
| 938 | |
| 939 | err_handler, main_entry, return_entry, finally_entry = ( |
| 940 | BasicBlock(), |
| 941 | BasicBlock(), |
| 942 | BasicBlock(), |
| 943 | BasicBlock(), |
| 944 | ) |
| 945 | |
| 946 | # Track if we're returning from the try block |
| 947 | control = TryFinallyNonlocalControl(return_entry) |
| 948 | builder.builder.push_error_handler(err_handler) |
| 949 | builder.nonlocal_control.append(control) |
| 950 | builder.goto_and_activate(BasicBlock()) |
| 951 | try_body() |
| 952 | builder.goto(main_entry) |
| 953 | builder.nonlocal_control.pop() |
| 954 | builder.builder.pop_error_handler() |
| 955 | ret_reg = control.ret_reg |
| 956 | |
| 957 | # Normal case - no exception or return |
| 958 | builder.activate_block(main_entry) |
| 959 | builder.goto(finally_entry) |
| 960 | |
| 961 | # Return case |
| 962 | builder.activate_block(return_entry) |
| 963 | builder.goto(finally_entry) |
| 964 | |
| 965 | # Exception case - need to catch to clear the error indicator |
| 966 | builder.activate_block(err_handler) |
| 967 | # Catch the error to clear Python's error indicator |
| 968 | builder.call_c(error_catch_op, [], line) |
| 969 | # We're not going to use old_exc since it won't survive await |
| 970 | # The exception is now in sys.exc_info() |
| 971 | builder.goto(finally_entry) |
| 972 | |
| 973 | # Finally block |
| 974 | builder.activate_block(finally_entry) |
| 975 | |
| 976 | # Execute finally body |
| 977 | finally_body() |
| 978 | |
| 979 | # After finally, we need to handle exceptions carefully: |
| 980 | # 1. If finally raised a new exception, it's in the error indicator - let it propagate |
| 981 | # 2. If finally didn't raise, check if we need to reraise the original from sys.exc_info() |
| 982 | # 3. If there was a return, return that value |
| 983 | # 4. Otherwise, normal exit |
| 984 | |
| 985 | # First, check if there's a current exception in the error indicator |
| 986 | # (this would be from the finally block) |
no test coverage detected
searching dependent graphs…