Example #1
0
def buildAwaitNode(provider, node, source_ref):
    return ExpressionYieldFromWaitable(
        expression=ExpressionAsyncWait(
            expression=buildNode(provider, node.value, source_ref),
            source_ref=source_ref,
        ),
        source_ref=source_ref,
    )
def _makeIteratorNext(qual, iterator_ref, source_ref):
    if getattr(qual, "is_async", 0):
        return ExpressionYieldFromWaitable(
            expression=ExpressionAsyncNext(value=iterator_ref, source_ref=source_ref),
            source_ref=source_ref,
        )
    else:
        return ExpressionBuiltinNext1(value=iterator_ref, source_ref=source_ref)
def _makeIteratorCreation(provider, qual, for_asyncgen, source_ref):
    if getattr(qual, "is_async", 0):
        result = ExpressionAsyncIter(value=buildNode(provider=provider,
                                                     node=qual.iter,
                                                     source_ref=source_ref),
                                     source_ref=source_ref)

        if not for_asyncgen or python_version < 370:
            result = ExpressionYieldFromWaitable(expression=result,
                                                 source_ref=source_ref)

        return result
    else:
        return ExpressionBuiltinIter1(value=buildNode(provider=provider,
                                                      node=qual.iter,
                                                      source_ref=source_ref),
                                      source_ref=source_ref)
Example #4
0
def _buildWithNode(provider, context_expr, assign_target, body, sync,
                   source_ref):
    # Many details, pylint: disable=too-many-locals
    with_source = buildNode(provider, context_expr, source_ref)

    if python_version < 380 and Options.isFullCompat():
        source_ref = with_source.getCompatibleSourceReference()

    temp_scope = provider.allocateTempScope("with")

    tmp_source_variable = provider.allocateTempVariable(temp_scope=temp_scope,
                                                        name="source")
    tmp_exit_variable = provider.allocateTempVariable(temp_scope=temp_scope,
                                                      name="exit")
    tmp_enter_variable = provider.allocateTempVariable(temp_scope=temp_scope,
                                                       name="enter")
    tmp_indicator_variable = provider.allocateTempVariable(
        temp_scope=temp_scope, name="indicator")

    statements = (
        buildAssignmentStatements(
            provider=provider,
            node=assign_target,
            allow_none=True,
            source=ExpressionTempVariableRef(variable=tmp_enter_variable,
                                             source_ref=source_ref),
            source_ref=source_ref,
        ),
        body,
    )

    with_body = makeStatementsSequence(statements=statements,
                                       allow_none=True,
                                       source_ref=source_ref)

    if body:
        deepest = body

        while deepest.getVisitableNodes():
            deepest = deepest.getVisitableNodes()[-1]

        if python_version < 370:
            body_lineno = deepest.getCompatibleSourceReference().getLineNumber(
            )
        else:
            body_lineno = deepest.getSourceReference().getLineNumber()

        with_exit_source_ref = source_ref.atLineNumber(body_lineno)
    else:
        with_exit_source_ref = source_ref

    # The "__enter__" and "__exit__" were normal attribute lookups under
    # CPython2.6, but that changed with CPython2.7.
    if python_version < 270:
        attribute_lookup_class = ExpressionAttributeLookup
    else:
        attribute_lookup_class = ExpressionAttributeLookupSpecial

    enter_value = ExpressionCallEmpty(
        called=attribute_lookup_class(
            source=ExpressionTempVariableRef(variable=tmp_source_variable,
                                             source_ref=source_ref),
            attribute_name="__enter__" if sync else "__aenter__",
            source_ref=source_ref,
        ),
        source_ref=source_ref,
    )

    exit_value_exception = ExpressionCallNoKeywords(
        called=ExpressionTempVariableRef(variable=tmp_exit_variable,
                                         source_ref=with_exit_source_ref),
        args=ExpressionMakeTuple(
            elements=(
                ExpressionCaughtExceptionTypeRef(
                    source_ref=with_exit_source_ref),
                ExpressionCaughtExceptionValueRef(
                    source_ref=with_exit_source_ref),
                ExpressionCaughtExceptionTracebackRef(source_ref=source_ref),
            ),
            source_ref=source_ref,
        ),
        source_ref=with_exit_source_ref,
    )

    exit_value_no_exception = ExpressionCallNoKeywords(
        called=ExpressionTempVariableRef(variable=tmp_exit_variable,
                                         source_ref=source_ref),
        args=makeConstantRefNode(constant=(None, None, None),
                                 source_ref=source_ref),
        source_ref=with_exit_source_ref,
    )

    # For "async with", await the entered value and exit value must be awaited.
    if not sync:
        enter_value = ExpressionYieldFromWaitable(
            expression=ExpressionAsyncWaitEnter(expression=enter_value,
                                                source_ref=source_ref),
            source_ref=source_ref,
        )
        exit_value_exception = ExpressionYieldFromWaitable(
            expression=ExpressionAsyncWaitExit(expression=exit_value_exception,
                                               source_ref=source_ref),
            source_ref=source_ref,
        )
        exit_value_no_exception = ExpressionYieldFromWaitable(
            ExpressionAsyncWaitExit(expression=exit_value_no_exception,
                                    source_ref=source_ref),
            source_ref=source_ref,
        )

    statements = [
        # First assign the with context to a temporary variable.
        StatementAssignmentVariable(variable=tmp_source_variable,
                                    source=with_source,
                                    source_ref=source_ref)
    ]

    attribute_assignments = [
        # Next, assign "__enter__" and "__exit__" attributes to temporary
        # variables.
        StatementAssignmentVariable(
            variable=tmp_exit_variable,
            source=attribute_lookup_class(
                source=ExpressionTempVariableRef(variable=tmp_source_variable,
                                                 source_ref=source_ref),
                attribute_name="__exit__" if sync else "__aexit__",
                source_ref=source_ref,
            ),
            source_ref=source_ref,
        ),
        StatementAssignmentVariable(variable=tmp_enter_variable,
                                    source=enter_value,
                                    source_ref=source_ref),
    ]

    if python_version >= 360 and sync:
        attribute_assignments.reverse()

    statements += attribute_assignments

    statements.append(
        StatementAssignmentVariable(
            variable=tmp_indicator_variable,
            source=makeConstantRefNode(constant=True, source_ref=source_ref),
            source_ref=source_ref,
        ))

    statements += [
        makeTryFinallyStatement(
            provider=provider,
            tried=makeTryExceptSingleHandlerNodeWithPublish(
                provider=provider,
                tried=with_body,
                exception_name="BaseException",
                handler_body=StatementsSequence(
                    statements=(
                        # Prevents final block from calling __exit__ as
                        # well.
                        StatementAssignmentVariable(
                            variable=tmp_indicator_variable,
                            source=makeConstantRefNode(constant=False,
                                                       source_ref=source_ref),
                            source_ref=source_ref,
                        ),
                        makeStatementConditional(
                            condition=exit_value_exception,
                            no_branch=makeReraiseExceptionStatement(
                                source_ref=with_exit_source_ref),
                            yes_branch=None,
                            source_ref=with_exit_source_ref,
                        ),
                    ),
                    source_ref=source_ref,
                ),
                public_exc=python_version >= 270,
                source_ref=source_ref,
            ),
            final=makeStatementConditional(
                condition=ExpressionComparisonIs(
                    left=ExpressionTempVariableRef(
                        variable=tmp_indicator_variable,
                        source_ref=source_ref),
                    right=makeConstantRefNode(constant=True,
                                              source_ref=source_ref),
                    source_ref=source_ref,
                ),
                yes_branch=StatementExpressionOnly(
                    expression=exit_value_no_exception, source_ref=source_ref),
                no_branch=None,
                source_ref=source_ref,
            ),
            source_ref=source_ref,
        )
    ]

    return makeTryFinallyStatement(
        provider=provider,
        tried=statements,
        final=(
            StatementReleaseVariable(variable=tmp_source_variable,
                                     source_ref=with_exit_source_ref),
            StatementReleaseVariable(variable=tmp_enter_variable,
                                     source_ref=with_exit_source_ref),
            StatementReleaseVariable(variable=tmp_exit_variable,
                                     source_ref=with_exit_source_ref),
            StatementReleaseVariable(variable=tmp_indicator_variable,
                                     source_ref=with_exit_source_ref),
        ),
        source_ref=source_ref,
    )
def _buildContractionBodyNode(
    provider,
    node,
    emit_class,
    start_value,
    container_tmp,
    iter_tmp,
    temp_scope,
    assign_provider,
    function_body,
    for_asyncgen,
    source_ref,
):

    # This uses lots of variables and branches. There is no good way
    # around that, and we deal with many cases, due to having generator
    # expressions sharing this code, pylint: disable=too-many-branches,too-many-locals

    # Note: The assign_provider is only to cover Python2 list contractions,
    # assigning one of the loop variables to the outside scope.

    tmp_variables = []
    if emit_class is not ExpressionYield:
        tmp_variables.append(iter_tmp)

    if container_tmp is not None:
        tmp_variables.append(container_tmp)

    statements = []

    # First assign the iterator if we are an outline.
    if assign_provider:
        statements.append(
            StatementAssignmentVariable(
                variable=iter_tmp,
                source=_makeIteratorCreation(
                    provider=provider,
                    qual=node.generators[0],
                    for_asyncgen=False,
                    source_ref=source_ref,
                ),
                source_ref=source_ref.atInternal(),
            ))

    if for_asyncgen and python_version >= 370 and node.generators[0].is_async:
        statements.append(
            StatementAssignmentVariable(
                variable=iter_tmp,
                source=ExpressionTempVariableRef(variable=iter_tmp,
                                                 source_ref=source_ref),
                source_ref=source_ref,
            ))

    if start_value is not None:
        statements.append(
            StatementAssignmentVariable(
                variable=container_tmp,
                source=makeConstantRefNode(constant=start_value,
                                           source_ref=source_ref),
                source_ref=source_ref.atInternal(),
            ))

    if hasattr(node, "elt"):
        if start_value is not None:
            current_body = emit_class(
                ExpressionTempVariableRef(variable=container_tmp,
                                          source_ref=source_ref),
                buildNode(
                    provider=function_body
                    if not assign_provider else provider,
                    node=node.elt,
                    source_ref=source_ref,
                ),
                source_ref=source_ref,
            )
        else:
            assert emit_class is ExpressionYield

            current_body = emit_class(
                buildNode(provider=function_body,
                          node=node.elt,
                          source_ref=source_ref),
                source_ref=source_ref,
            )
    else:
        current_body = emit_class(
            dict_arg=ExpressionTempVariableRef(variable=container_tmp,
                                               source_ref=source_ref),
            key=buildNode(
                provider=function_body if not assign_provider else provider,
                node=node.key,
                source_ref=source_ref,
            ),
            value=buildNode(
                provider=function_body if not assign_provider else provider,
                node=node.value,
                source_ref=source_ref,
            ),
            source_ref=source_ref,
        )

    if current_body.isExpression():
        current_body = StatementExpressionOnly(expression=current_body,
                                               source_ref=source_ref)

    for count, qual in enumerate(reversed(node.generators)):
        tmp_value_variable = function_body.allocateTempVariable(
            temp_scope=temp_scope, name="iter_value_%d" % count)

        tmp_variables.append(tmp_value_variable)

        # The first iterated value is to be calculated outside of the function
        # and will be given as a parameter "_iterated", the others are built
        # inside the function.

        if qual is node.generators[0]:
            iterator_ref = makeVariableRefNode(variable=iter_tmp,
                                               source_ref=source_ref)

            if for_asyncgen and python_version >= 370:
                iterator_ref = ExpressionYieldFromWaitable(
                    expression=iterator_ref, source_ref=source_ref)

            tmp_iter_variable = None

            nested_statements = []
        else:
            # First create the iterator and store it, next should be loop body
            value_iterator = _makeIteratorCreation(
                provider=provider if assign_provider else function_body,
                qual=qual,
                for_asyncgen=False,
                source_ref=source_ref,
            )

            tmp_iter_variable = function_body.allocateTempVariable(
                temp_scope=temp_scope, name="contraction_iter_%d" % count)

            tmp_variables.append(tmp_iter_variable)

            nested_statements = [
                StatementAssignmentVariable(
                    variable=tmp_iter_variable,
                    source=value_iterator,
                    source_ref=source_ref,
                )
            ]

            iterator_ref = ExpressionTempVariableRef(
                variable=tmp_iter_variable, source_ref=source_ref)

        loop_statements = [
            makeTryExceptSingleHandlerNode(
                tried=StatementAssignmentVariable(
                    variable=tmp_value_variable,
                    source=_makeIteratorNext(iterator_ref=iterator_ref,
                                             qual=qual,
                                             source_ref=source_ref),
                    source_ref=source_ref,
                ),
                exception_name=_getStopIterationName(qual),
                handler_body=StatementLoopBreak(source_ref=source_ref),
                source_ref=source_ref,
            ),
            buildAssignmentStatements(
                provider=provider if assign_provider else function_body,
                temp_provider=function_body,
                node=qual.target,
                source=ExpressionTempVariableRef(variable=tmp_value_variable,
                                                 source_ref=source_ref),
                source_ref=source_ref,
            ),
        ]

        conditions = buildNodeList(
            provider=provider if assign_provider else function_body,
            nodes=qual.ifs,
            source_ref=source_ref,
        )

        if len(conditions) >= 1:
            loop_statements.append(
                makeStatementConditional(
                    condition=buildAndNode(values=conditions,
                                           source_ref=source_ref),
                    yes_branch=current_body,
                    no_branch=None,
                    source_ref=source_ref,
                ))
        else:
            loop_statements.append(current_body)

        nested_statements.append(
            StatementLoop(
                body=StatementsSequence(
                    statements=mergeStatements(loop_statements),
                    source_ref=source_ref),
                source_ref=source_ref,
            ))

        if tmp_iter_variable is not None:
            nested_statements.append(
                StatementReleaseVariable(variable=tmp_iter_variable,
                                         source_ref=source_ref))

        current_body = StatementsSequence(statements=mergeStatements(
            nested_statements, False),
                                          source_ref=source_ref)

    statements.append(current_body)
    statements = mergeStatements(statements)

    release_statements = [
        StatementReleaseVariable(variable=tmp_variable, source_ref=source_ref)
        for tmp_variable in tmp_variables
    ]

    return statements, release_statements
def _buildForLoopNode(provider, node, sync, source_ref):
    # The for loop is re-formulated according to developer manual. An iterator
    # is created, and looped until it gives StopIteration. The else block is
    # taken if a for loop exits normally, i.e. because of iterator
    # exhaustion. We do this by introducing an indicator variable.

    # We handle async and sync both here, leading to cases, pylint: disable=too-many-locals

    source = buildNode(provider, node.iter, source_ref)

    # Temporary variables, we need one for the iterator, and one for the current
    # value.
    temp_scope = provider.allocateTempScope("for_loop")

    tmp_iter_variable = provider.allocateTempVariable(temp_scope=temp_scope,
                                                      name="for_iterator")
    tmp_value_variable = provider.allocateTempVariable(temp_scope=temp_scope,
                                                       name="iter_value")

    else_block = buildStatementsNode(
        provider=provider,
        nodes=node.orelse if node.orelse else None,
        source_ref=source_ref,
    )

    if else_block is not None:
        # Indicator variable, will end up with C bool type, and need not be released.
        tmp_break_indicator = provider.allocateTempVariable(
            temp_scope=temp_scope, name="break_indicator", temp_type="bool")

        statements = [
            StatementAssignmentVariable(
                variable=tmp_break_indicator,
                source=makeConstantRefNode(constant=True,
                                           source_ref=source_ref),
                source_ref=source_ref,
            )
        ]
    else:
        statements = []

    statements.append(StatementLoopBreak(source_ref=source_ref))

    handler_body = makeStatementsSequence(statements=statements,
                                          allow_none=False,
                                          source_ref=source_ref)

    if sync:
        next_node = ExpressionBuiltinNext1(
            value=ExpressionTempVariableRef(variable=tmp_iter_variable,
                                            source_ref=source_ref),
            source_ref=source_ref,
        )
    else:
        next_node = ExpressionYieldFromWaitable(
            expression=ExpressionAsyncNext(
                value=ExpressionTempVariableRef(variable=tmp_iter_variable,
                                                source_ref=source_ref),
                source_ref=source_ref,
            ),
            source_ref=source_ref,
        )

    statements = (
        makeTryExceptSingleHandlerNode(
            tried=StatementAssignmentVariable(variable=tmp_value_variable,
                                              source=next_node,
                                              source_ref=source_ref),
            exception_name="StopIteration" if sync else "StopAsyncIteration",
            handler_body=handler_body,
            source_ref=source_ref,
        ),
        buildAssignmentStatements(
            provider=provider,
            node=node.target,
            source=ExpressionTempVariableRef(variable=tmp_value_variable,
                                             source_ref=source_ref),
            source_ref=source_ref,
        ),
    )

    pushBuildContext("loop_body")
    statements += (buildStatementsNode(provider=provider,
                                       nodes=node.body,
                                       source_ref=source_ref), )
    popBuildContext()

    loop_body = makeStatementsSequence(statements=statements,
                                       allow_none=True,
                                       source_ref=source_ref)

    cleanup_statements = [
        StatementReleaseVariable(variable=tmp_value_variable,
                                 source_ref=source_ref),
        StatementReleaseVariable(variable=tmp_iter_variable,
                                 source_ref=source_ref),
    ]

    if else_block is not None:
        statements = [
            StatementAssignmentVariable(
                variable=tmp_break_indicator,
                source=makeConstantRefNode(constant=False,
                                           source_ref=source_ref),
                source_ref=source_ref,
            )
        ]
    else:
        statements = []

    if sync:
        iter_source = ExpressionBuiltinIter1(
            value=source, source_ref=source.getSourceReference())
    else:
        iter_source = ExpressionYieldFromWaitable(
            expression=ExpressionAsyncIter(
                value=source, source_ref=source.getSourceReference()),
            source_ref=source.getSourceReference(),
        )

    statements += (
        # First create the iterator and store it.
        StatementAssignmentVariable(variable=tmp_iter_variable,
                                    source=iter_source,
                                    source_ref=source_ref),
        makeTryFinallyStatement(
            provider=provider,
            tried=StatementLoop(loop_body=loop_body, source_ref=source_ref),
            final=StatementsSequence(statements=cleanup_statements,
                                     source_ref=source_ref),
            source_ref=source_ref,
        ),
    )

    if else_block is not None:
        statements.append(
            makeStatementConditional(
                condition=ExpressionComparisonIs(
                    left=ExpressionTempVariableRef(
                        variable=tmp_break_indicator, source_ref=source_ref),
                    right=makeConstantRefNode(constant=True,
                                              source_ref=source_ref),
                    source_ref=source_ref,
                ),
                yes_branch=else_block,
                no_branch=None,
                source_ref=source_ref,
            ))

    return makeStatementsSequenceFromStatements(*statements)
def _buildWithNode(provider, context_expr, assign_target, body, sync, source_ref):
    # Many details, pylint: disable=too-many-branches,too-many-locals
    with_source = buildNode(provider, context_expr, source_ref)

    if python_version < 0x380 and Options.is_fullcompat:
        source_ref = with_source.getCompatibleSourceReference()

    temp_scope = provider.allocateTempScope("with")

    tmp_source_variable = provider.allocateTempVariable(
        temp_scope=temp_scope, name="source"
    )
    tmp_exit_variable = provider.allocateTempVariable(
        temp_scope=temp_scope, name="exit"
    )
    tmp_enter_variable = provider.allocateTempVariable(
        temp_scope=temp_scope, name="enter"
    )

    # Indicator variable, will end up with C bool type, and need not be released.
    tmp_indicator_variable = provider.allocateTempVariable(
        temp_scope=temp_scope, name="indicator", temp_type="bool"
    )

    statements = (
        buildAssignmentStatements(
            provider=provider,
            node=assign_target,
            allow_none=True,
            source=ExpressionTempVariableRef(
                variable=tmp_enter_variable, source_ref=source_ref
            ),
            source_ref=source_ref,
        ),
        body,
    )

    with_body = makeStatementsSequence(
        statements=statements, allow_none=True, source_ref=source_ref
    )

    if body and python_version < 0x3A0:
        deepest = body

        while deepest.getVisitableNodes():
            deepest = deepest.getVisitableNodes()[-1]

        if python_version < 0x370:
            body_lineno = deepest.getCompatibleSourceReference().getLineNumber()
        else:
            body_lineno = deepest.getSourceReference().getLineNumber()

        with_exit_source_ref = source_ref.atLineNumber(body_lineno)
    else:
        with_exit_source_ref = source_ref

    # The "__enter__" and "__exit__" were normal attribute lookups under
    # CPython2.6, but that changed with CPython2.7.
    if python_version < 0x270:
        attribute_lookup_maker = makeExpressionAttributeLookup
    else:
        attribute_lookup_maker = ExpressionAttributeLookupSpecial

    enter_value = ExpressionCallEmpty(
        called=attribute_lookup_maker(
            expression=ExpressionTempVariableRef(
                variable=tmp_source_variable, source_ref=source_ref
            ),
            attribute_name="__enter__" if sync else "__aenter__",
            source_ref=source_ref,
        ),
        source_ref=source_ref,
    )

    exit_value_exception = ExpressionCallNoKeywords(
        called=ExpressionTempVariableRef(
            variable=tmp_exit_variable, source_ref=with_exit_source_ref
        ),
        args=makeExpressionMakeTuple(
            elements=(
                ExpressionCaughtExceptionTypeRef(source_ref=with_exit_source_ref),
                ExpressionCaughtExceptionValueRef(source_ref=with_exit_source_ref),
                ExpressionCaughtExceptionTracebackRef(source_ref=source_ref),
            ),
            source_ref=source_ref,
        ),
        source_ref=with_exit_source_ref,
    )

    exit_value_no_exception = ExpressionCallNoKeywords(
        called=ExpressionTempVariableRef(
            variable=tmp_exit_variable, source_ref=source_ref
        ),
        args=makeConstantRefNode(constant=(None, None, None), source_ref=source_ref),
        source_ref=with_exit_source_ref,
    )

    # For "async with", await the entered value and exit value must be awaited.
    if not sync:
        exit_value_exception = ExpressionYieldFromWaitable(
            expression=ExpressionAsyncWaitExit(
                expression=exit_value_exception, source_ref=source_ref
            ),
            source_ref=source_ref,
        )
        exit_value_no_exception = ExpressionYieldFromWaitable(
            ExpressionAsyncWaitExit(
                expression=exit_value_no_exception, source_ref=source_ref
            ),
            source_ref=source_ref,
        )

    # First assign the with context to a temporary variable.
    statements = [
        StatementAssignmentVariable(
            variable=tmp_source_variable, source=with_source, source_ref=source_ref
        )
    ]

    # Before 3.9, __aenter__ is immediately awaited, after we first do __aexit__ lookup.
    if not sync and python_version < 0x390:
        enter_value = ExpressionYieldFromWaitable(
            expression=ExpressionAsyncWaitEnter(
                expression=enter_value, source_ref=source_ref
            ),
            source_ref=source_ref,
        )

    attribute_enter_assignment = StatementAssignmentVariable(
        variable=tmp_enter_variable, source=enter_value, source_ref=source_ref
    )

    attribute_exit_assignment = StatementAssignmentVariable(
        variable=tmp_exit_variable,
        source=attribute_lookup_maker(
            expression=ExpressionTempVariableRef(
                variable=tmp_source_variable, source_ref=source_ref
            ),
            attribute_name="__exit__" if sync else "__aexit__",
            source_ref=source_ref,
        ),
        source_ref=source_ref,
    )

    # Next, assign "__enter__" and "__exit__" attributes to temporary variables, and
    # depending on Python versions switch the order of these lookups and the order of
    # awaiting enter.
    # Normal "with" statements are enter, exit ordered after 3.6, and "async with"
    # are since 3.9, and since 3.9 the enter is not awaited, until an exit is present.
    if python_version >= 0x390 and not sync:
        enter_await_statement = StatementAssignmentVariable(
            variable=tmp_enter_variable,
            source=ExpressionYieldFromWaitable(
                expression=ExpressionAsyncWaitEnter(
                    expression=ExpressionTempVariableRef(
                        variable=tmp_enter_variable, source_ref=source_ref
                    ),
                    source_ref=source_ref,
                ),
                source_ref=source_ref,
            ),
            source_ref=source_ref,
        )

        attribute_assignments = (
            attribute_enter_assignment,
            attribute_exit_assignment,
            enter_await_statement,
        )
    elif python_version >= 0x360 and sync:
        attribute_assignments = (attribute_enter_assignment, attribute_exit_assignment)
    else:
        attribute_assignments = (attribute_exit_assignment, attribute_enter_assignment)

    statements.extend(attribute_assignments)

    statements.append(
        StatementAssignmentVariable(
            variable=tmp_indicator_variable,
            source=makeConstantRefNode(constant=True, source_ref=source_ref),
            source_ref=source_ref,
        )
    )

    statements += (
        makeTryFinallyStatement(
            provider=provider,
            tried=makeTryExceptSingleHandlerNodeWithPublish(
                provider=provider,
                tried=with_body,
                exception_name="BaseException",
                handler_body=StatementsSequence(
                    statements=(
                        # Prevents final block from calling __exit__ as
                        # well.
                        StatementAssignmentVariable(
                            variable=tmp_indicator_variable,
                            source=makeConstantRefNode(
                                constant=False, source_ref=source_ref
                            ),
                            source_ref=source_ref,
                        ),
                        makeStatementConditional(
                            condition=exit_value_exception,
                            no_branch=makeReraiseExceptionStatement(
                                source_ref=with_exit_source_ref
                            ),
                            yes_branch=None,
                            source_ref=with_exit_source_ref,
                        ),
                    ),
                    source_ref=source_ref,
                ),
                public_exc=python_version >= 0x270,
                source_ref=source_ref,
            ),
            final=makeStatementConditional(
                condition=ExpressionComparisonIs(
                    left=ExpressionTempVariableRef(
                        variable=tmp_indicator_variable, source_ref=source_ref
                    ),
                    right=makeConstantRefNode(constant=True, source_ref=source_ref),
                    source_ref=source_ref,
                ),
                yes_branch=StatementExpressionOnly(
                    expression=exit_value_no_exception, source_ref=source_ref
                ),
                no_branch=None,
                source_ref=source_ref,
            ),
            source_ref=source_ref,
        ),
    )

    return makeTryFinallyStatement(
        provider=provider,
        tried=statements,
        final=(
            StatementReleaseVariable(
                variable=tmp_source_variable, source_ref=with_exit_source_ref
            ),
            StatementReleaseVariable(
                variable=tmp_enter_variable, source_ref=with_exit_source_ref
            ),
            StatementReleaseVariable(
                variable=tmp_exit_variable, source_ref=with_exit_source_ref
            ),
        ),
        source_ref=source_ref,
    )