def _buildWithNode(provider, context_expr, assign_target, body, source_ref):
    with_source = buildNode(provider, context_expr, source_ref)

    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 Options.isFullCompat() and with_body is not None:
        with_exit_source_ref = with_body.getStatements()[-1].\
          getSourceReference()
    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 Utils.python_version < 270:
        attribute_lookup_class = ExpressionAttributeLookup
    else:
        attribute_lookup_class = ExpressionSpecialAttributeLookup

    statements = [
        # First assign the with context to a temporary variable.
        StatementAssignmentVariable(
            variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_source_variable, source_ref=source_ref),
            source=with_source,
            source_ref=source_ref),
        # Next, assign "__enter__" and "__exit__" attributes to temporary
        # variables.
        StatementAssignmentVariable(
            variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_exit_variable, source_ref=source_ref),
            source=attribute_lookup_class(source=ExpressionTempVariableRef(
                variable=tmp_source_variable, source_ref=source_ref),
                                          attribute_name="__exit__",
                                          source_ref=source_ref),
            source_ref=source_ref),
        StatementAssignmentVariable(
            variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_enter_variable, source_ref=source_ref),
            source=ExpressionCallEmpty(called=attribute_lookup_class(
                source=ExpressionTempVariableRef(variable=tmp_source_variable,
                                                 source_ref=source_ref),
                attribute_name="__enter__",
                source_ref=source_ref),
                                       source_ref=source_ref),
            source_ref=source_ref),
        StatementAssignmentVariable(
            variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_indicator_variable, source_ref=source_ref),
            source=ExpressionConstantRef(constant=True, source_ref=source_ref),
            source_ref=source_ref),
    ]

    source_ref = source_ref.atInternal()

    statements += [
        makeTryFinallyStatement(
            tried=makeTryExceptSingleHandlerNode(
                tried=with_body,
                exception_name="BaseException",
                handler_body=StatementsSequence(
                    statements=(
                        # Prevents final block from calling __exit__ as
                        # well.
                        StatementAssignmentVariable(
                            variable_ref=ExpressionTargetTempVariableRef(
                                variable=tmp_indicator_variable,
                                source_ref=source_ref),
                            source=ExpressionConstantRef(
                                constant=False, source_ref=source_ref),
                            source_ref=source_ref),
                        StatementConditional(
                            condition=ExpressionCallNoKeywords(
                                called=ExpressionTempVariableRef(
                                    variable=tmp_exit_variable,
                                    source_ref=source_ref),
                                args=ExpressionMakeTuple(
                                    elements=(
                                        ExpressionCaughtExceptionTypeRef(
                                            source_ref=source_ref),
                                        ExpressionCaughtExceptionValueRef(
                                            source_ref=source_ref),
                                        ExpressionCaughtExceptionTracebackRef(
                                            source_ref=source_ref),
                                    ),
                                    source_ref=source_ref),
                                source_ref=source_ref),
                            no_branch=makeStatementsSequenceFromStatement(
                                statement=StatementRaiseException(
                                    exception_type=None,
                                    exception_value=None,
                                    exception_trace=None,
                                    exception_cause=None,
                                    source_ref=source_ref)),
                            yes_branch=None,
                            source_ref=source_ref),
                    ),
                    source_ref=source_ref),
                public_exc=Utils.python_version >= 270,
                source_ref=source_ref),
            final=StatementConditional(
                condition=ExpressionComparisonIs(
                    left=ExpressionTempVariableRef(
                        variable=tmp_indicator_variable,
                        source_ref=source_ref),
                    right=ExpressionConstantRef(constant=True,
                                                source_ref=source_ref),
                    source_ref=source_ref),
                yes_branch=makeStatementsSequenceFromStatement(
                    statement=StatementExpressionOnly(
                        expression=ExpressionCallNoKeywords(
                            called=ExpressionTempVariableRef(
                                variable=tmp_exit_variable,
                                source_ref=source_ref),
                            args=ExpressionConstantRef(constant=(None, None,
                                                                 None),
                                                       source_ref=source_ref),
                            source_ref=with_exit_source_ref),
                        source_ref=source_ref)),
                no_branch=None,
                source_ref=source_ref),
            source_ref=source_ref)
    ]

    return makeTryFinallyStatement(
        tried=statements,
        final=(
            StatementDelVariable(variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_source_variable, source_ref=source_ref),
                                 tolerant=True,
                                 source_ref=source_ref),
            StatementDelVariable(variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_enter_variable, source_ref=source_ref),
                                 tolerant=True,
                                 source_ref=source_ref),
            StatementDelVariable(variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_exit_variable, source_ref=source_ref),
                                 tolerant=True,
                                 source_ref=source_ref),
            StatementDelVariable(variable_ref=ExpressionTargetTempVariableRef(
                variable=tmp_indicator_variable, source_ref=source_ref),
                                 tolerant=True,
                                 source_ref=source_ref),
        ),
        source_ref=source_ref)
예제 #2
0
def _buildWithNode(provider, context_expr, assign_target, body, body_lineno,
                   sync, source_ref):
    # Many details, pylint: disable=too-many-locals
    with_source = buildNode(provider, context_expr, source_ref)

    if 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 Options.isFullCompat():
        if body:
            deepest = body

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

            body_lineno = deepest.getCompatibleSourceReference().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 = ExpressionAsyncWait(
            expression = enter_value,
            source_ref = source_ref
        )
        exit_value_exception = ExpressionAsyncWait(
            expression = exit_value_exception,
            source_ref = source_ref
        )
        exit_value_no_exception = ExpressionAsyncWait(
            expression = exit_value_no_exception,
            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
                        ),
                        makeConditionalStatement(
                            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      = StatementConditional(
                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 = makeStatementsSequenceFromStatement(
                    statement = 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
    )
예제 #3
0
def buildTryExceptionNode(provider, node, source_ref):
    # Try/except nodes. Re-formulated as described in the developer
    # manual. Exception handlers made the assignment to variables explicit. Same
    # for the "del" as done for Python3. Also catches always work a tuple of
    # exception types and hides away that they may be built or not.

    # Many variables and branches, due to the re-formulation that is going on
    # here, which just has the complexity, pylint: disable=R0912,R0914

    tried = buildStatementsNode(
        provider   = provider,
        nodes      = node.body,
        source_ref = source_ref
    )

    handlers = []

    for handler in node.handlers:
        exception_expression, exception_assign, exception_block = (
            handler.type,
            handler.name,
            handler.body
        )

        if exception_assign is None:
            statements = [
                buildStatementsNode(
                    provider   = provider,
                    nodes      = exception_block,
                    source_ref = source_ref
                )
            ]
        elif Utils.python_version < 300:
            statements = [
                buildAssignmentStatements(
                    provider   = provider,
                    node       = exception_assign,
                    source     = ExpressionCaughtExceptionValueRef(
                        source_ref = source_ref.atInternal()
                    ),
                    source_ref = source_ref.atInternal()
                ),
                buildStatementsNode(
                    provider   = provider,
                    nodes      = exception_block,
                    source_ref = source_ref
                )
            ]
        else:
            target_info = decodeAssignTarget(
                provider   = provider,
                node       = exception_assign,
                source_ref = source_ref,
            )

            # We didn't allow None, therefore it cannot be None, and
            # the unpack is safe: pylint: disable=W0633
            kind, detail = target_info

            assert kind == "Name", kind
            kind = "Name_Exception"

            statements = [
                buildAssignmentStatements(
                    provider   = provider,
                    node       = exception_assign,
                    source     = ExpressionCaughtExceptionValueRef(
                        source_ref = source_ref.atInternal()
                    ),
                    source_ref = source_ref.atInternal()
                ),
                makeTryFinallyStatement(
                    tried      = buildStatementsNode(
                        provider   = provider,
                        nodes      = exception_block,
                        source_ref = source_ref
                    ),
                    final      = buildDeleteStatementFromDecoded(
                        kind       = kind,
                        detail     = detail,
                        source_ref = source_ref
                    ),
                    source_ref = source_ref
                )
            ]

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

        exception_types = buildNode(
            provider   = provider,
            node       = exception_expression,
            source_ref = source_ref,
            allow_none = True
        )

        # The exception types should be a tuple, so as to be most general.
        if exception_types is None:
            if handler is not node.handlers[-1]:
                SyntaxErrors.raiseSyntaxError(
                    reason     = "default 'except:' must be last",
                    source_ref = source_ref.atLineNumber(
                        handler.lineno-1
                          if Options.isFullCompat() else
                        handler.lineno
                    )
                )

        handlers.append(
            (
                exception_types,
                handler_body,
            )
        )

    # Reraise by default
    exception_handling = makeReraiseExceptionStatement(
        source_ref = source_ref
    )

    for exception_type, handler in reversed(handlers):
        if exception_type is None:
            # A default handler was given, so use that indead.
            exception_handling = handler
        else:
            exception_handling = StatementsSequence(
                statements = (
                    StatementConditional(
                        condition  = ExpressionComparisonExceptionMatch(
                            left       = ExpressionCaughtExceptionTypeRef(
                                source_ref = exception_type.source_ref
                            ),
                            right      = exception_type,
                            source_ref = exception_type.source_ref
                        ),
                        yes_branch = handler,
                        no_branch  = exception_handling,
                        source_ref = exception_type.source_ref
                    ),
                ),
                source_ref = exception_type.source_ref
            )

    prelude = (
        StatementPreserveFrameException(
            source_ref = source_ref.atInternal()
        ),
        StatementPublishException(
            source_ref = source_ref.atInternal()
        )
    )

    if exception_handling is None:
        # For Python3, we need not publish at all, if all we do is to revert
        # that immediately. For Python2, the publish may release previously
        # published exception, which has side effects potentially.
        if Utils.python_version < 300:
            exception_handling = StatementsSequence(
                statements = prelude,
                source_ref = source_ref.atInternal()
            )

            public_exc = True
        else:
            public_exc = False
    else:
        public_exc = True

        if Utils.python_version < 300:
            exception_handling.setStatements(
                prelude + exception_handling.getStatements()
            )
        else:
            exception_handling = StatementsSequence(
                statements = prelude + (
                    makeTryFinallyStatement(
                        tried      = exception_handling,
                        final      = StatementRestoreFrameException(
                            source_ref = source_ref.atInternal()
                        ),
                        source_ref = source_ref
                    ),
                ),
                source_ref = source_ref.atInternal()
            )

    no_raise = buildStatementsNode(
        provider   = provider,
        nodes      = node.orelse,
        source_ref = source_ref
    )

    if no_raise is None:
        return StatementTryExcept(
            tried      = tried,
            handling   = exception_handling,
            public_exc = public_exc,
            source_ref = source_ref
        )
    else:
        return makeTryExceptNoRaise(
            provider   = provider,
            temp_scope = provider.allocateTempScope("try_except"),
            handling   = exception_handling,
            tried      = tried,
            public_exc = public_exc,
            no_raise   = no_raise,
            source_ref = source_ref
        )
def buildTryExceptionNode(provider, node, source_ref):
    # Try/except nodes. Re-formulated as described in the developer
    # manual. Exception handlers made the assignment to variables explicit. Same
    # for the "del" as done for Python3. Also catches always work a tuple of
    # exception types and hides away that they may be built or not.

    # Many variables and branches, due to the re-formulation that is going on
    # here, which just has the complexity, pylint: disable=too-many-branches,too-many-locals

    tried = buildStatementsNode(provider=provider,
                                nodes=node.body,
                                source_ref=source_ref)

    handlers = []

    for handler in node.handlers:
        exception_expression, exception_assign, exception_block = (
            handler.type,
            handler.name,
            handler.body,
        )

        if exception_assign is None:
            statements = [
                buildStatementsNode(provider=provider,
                                    nodes=exception_block,
                                    source_ref=source_ref)
            ]
        elif python_version < 0x300:
            statements = [
                buildAssignmentStatements(
                    provider=provider,
                    node=exception_assign,
                    source=ExpressionCaughtExceptionValueRef(
                        source_ref=source_ref.atInternal()),
                    source_ref=source_ref.atInternal(),
                ),
                buildStatementsNode(provider=provider,
                                    nodes=exception_block,
                                    source_ref=source_ref),
            ]
        else:
            # Python3 requires temporary assignment of exception assignment.
            target_info = decodeAssignTarget(provider=provider,
                                             node=exception_assign,
                                             source_ref=source_ref)

            kind, detail = target_info

            assert kind == "Name", kind
            kind = "Name_Exception"

            statements = [
                buildAssignmentStatements(
                    provider=provider,
                    node=exception_assign,
                    source=ExpressionCaughtExceptionValueRef(
                        source_ref=source_ref.atInternal()),
                    source_ref=source_ref.atInternal(),
                ),
                makeTryFinallyStatement(
                    provider=provider,
                    tried=buildStatementsNode(provider=provider,
                                              nodes=exception_block,
                                              source_ref=source_ref),
                    final=buildDeleteStatementFromDecoded(
                        provider=provider,
                        kind=kind,
                        detail=detail,
                        source_ref=source_ref,
                    ),
                    source_ref=source_ref,
                ),
            ]

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

        exception_types = buildNode(
            provider=provider,
            node=exception_expression,
            source_ref=source_ref,
            allow_none=True,
        )

        # The exception types should be a tuple, so as to be most general.
        if exception_types is None:
            if handler is not node.handlers[-1]:
                raiseSyntaxError(
                    "default 'except:' must be last",
                    source_ref.atLineNumber(handler.lineno).atColumnNumber(
                        handler.col_offset),
                )

        handlers.append((exception_types, handler_body))

    # Re-raise by default
    exception_handling = makeReraiseExceptionStatement(source_ref=source_ref)

    for exception_type, handler in reversed(handlers):
        if exception_type is None:
            # A default handler was given, so use that indeed.
            exception_handling = handler
        else:
            exception_handling = StatementsSequence(
                statements=(makeStatementConditional(
                    condition=ExpressionComparisonExceptionMatch(
                        left=ExpressionCaughtExceptionTypeRef(
                            source_ref=exception_type.source_ref),
                        right=exception_type,
                        source_ref=exception_type.source_ref,
                    ),
                    yes_branch=handler,
                    no_branch=exception_handling,
                    source_ref=exception_type.source_ref,
                ), ),
                source_ref=exception_type.source_ref,
            )

    if exception_handling is None:
        # For Python3, we need not publish at all, if all we do is to revert
        # that immediately. For Python2, the publish may release previously
        # published exception, which has side effects potentially.
        if python_version < 0x300:
            exception_handling = StatementsSequence(
                statements=(
                    StatementPreserveFrameException(
                        preserver_id=0,  # unused with Python2
                        source_ref=source_ref.atInternal(),
                    ),
                    StatementPublishException(
                        source_ref=source_ref.atInternal()),
                ),
                source_ref=source_ref.atInternal(),
            )
    else:
        if python_version < 0x300:
            exception_handling.setChild(
                "statements",
                (
                    StatementPreserveFrameException(
                        preserver_id=0,  # unused with Python2
                        source_ref=source_ref.atInternal(),
                    ),
                    StatementPublishException(
                        source_ref=source_ref.atInternal()),
                ) + exception_handling.subnode_statements,
            )
        else:
            preserver_id = provider.allocatePreserverId()

            exception_handling = makeStatementsSequenceFromStatements(
                StatementPreserveFrameException(
                    preserver_id=preserver_id,
                    source_ref=source_ref.atInternal()),
                StatementPublishException(source_ref=source_ref.atInternal()),
                makeTryFinallyStatement(
                    provider=provider,
                    tried=exception_handling,
                    final=StatementRestoreFrameException(
                        preserver_id=preserver_id,
                        source_ref=source_ref.atInternal()),
                    source_ref=source_ref,
                ),
            )

    no_raise = buildStatementsNode(provider=provider,
                                   nodes=node.orelse,
                                   source_ref=source_ref)

    if no_raise is None:
        if tried is None:
            return None

        return StatementTry(
            tried=tried,
            except_handler=exception_handling,
            break_handler=None,
            continue_handler=None,
            return_handler=None,
            source_ref=source_ref,
        )
    else:
        if tried is None:
            return no_raise

        return makeTryExceptNoRaise(
            provider=provider,
            temp_scope=provider.allocateTempScope("try_except"),
            handling=exception_handling,
            tried=tried,
            no_raise=no_raise,
            source_ref=source_ref,
        )
def buildTryExceptionNode(provider, node, source_ref):
    # Try/except nodes. Re-formulated as described in the developer
    # manual. Exception handlers made the assignment to variables explicit. Same
    # for the "del" as done for Python3. Also catches always work a tuple of
    # exception types and hides away that they may be built or not.

    # Many variables, due to the re-formulation that is going on here, which
    # just has the complexity, pylint: disable=R0914

    handlers = []

    for handler in node.handlers:
        exception_expression, exception_assign, exception_block = (
            handler.type, handler.name, handler.body)

        statements = [
            buildAssignmentStatements(provider=provider,
                                      node=exception_assign,
                                      allow_none=True,
                                      source=ExpressionCaughtExceptionValueRef(
                                          source_ref=source_ref.atInternal()),
                                      source_ref=source_ref.atInternal()),
            buildStatementsNode(provider=provider,
                                nodes=exception_block,
                                source_ref=source_ref)
        ]

        if Utils.python_version >= 300:
            target_info = decodeAssignTarget(provider=provider,
                                             node=exception_assign,
                                             source_ref=source_ref,
                                             allow_none=True)

            if target_info is not None:
                kind, detail = target_info

                assert kind == "Name", kind
                kind = "Name_Exception"

                statements.append(
                    buildDeleteStatementFromDecoded(kind=kind,
                                                    detail=detail,
                                                    source_ref=source_ref))

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

        exception_types = buildNode(provider=provider,
                                    node=exception_expression,
                                    source_ref=source_ref,
                                    allow_none=True)

        # The exception types should be a tuple, so as to be most general.
        if exception_types is None:
            exception_types = ()

            if handler is not node.handlers[-1]:
                SyntaxErrors.raiseSyntaxError(
                    reason="default 'except:' must be last",
                    source_ref=source_ref.atLineNumber(
                        handler.lineno -
                        1 if Options.isFullCompat() else handler.lineno))
        elif exception_types.isExpressionMakeSequence():
            exception_types = exception_types.getElements()
        else:
            exception_types = (exception_types, )

        handlers.append(
            StatementExceptHandler(exception_types=exception_types,
                                   body=handler_body,
                                   source_ref=source_ref))

    tried = buildStatementsNode(provider=provider,
                                nodes=node.body,
                                source_ref=source_ref)

    no_raise = buildStatementsNode(provider=provider,
                                   nodes=node.orelse,
                                   source_ref=source_ref)

    if no_raise is None:
        return StatementTryExcept(handlers=handlers,
                                  tried=tried,
                                  source_ref=source_ref)
    else:
        return makeTryExceptNoRaise(
            provider=provider,
            temp_scope=provider.allocateTempScope("try_except"),
            handlers=handlers,
            tried=tried,
            no_raise=no_raise,
            source_ref=source_ref)
예제 #6
0
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,
    )
def buildTryExceptionNode( provider, node, source_ref ):
    # Try/except nodes. Re-formulated as described in the developer manual. Exception
    # handlers made the assignment to variables explicit. Same for the "del" as done for
    # Python3. Also catches always work a tuple of exception types and hides away that
    # they may be built or not.

    # Many variables, due to the re-formulation that is going on here, which just has the
    # complexity, pylint: disable=R0914

    handlers = []

    for handler in node.handlers:
        exception_expression, exception_assign, exception_block = handler.type, handler.name, handler.body

        statements = [
            buildAssignmentStatements(
                provider   = provider,
                node       = exception_assign,
                allow_none = True,
                source     = ExpressionCaughtExceptionValueRef(
                    source_ref = source_ref.atInternal()
                ),
                source_ref = source_ref.atInternal()
            ),
            buildStatementsNode(
                provider   = provider,
                nodes      = exception_block,
                source_ref = source_ref
            )
        ]

        if Utils.python_version >= 300:
            target_info = decodeAssignTarget( provider, exception_assign, source_ref, allow_none = True )

            if target_info is not None:
                kind, detail = target_info

                assert kind == "Name", kind
                kind = "Name_Exception"

                statements.append(
                    buildDeleteStatementFromDecoded(
                        kind       = kind,
                        detail     = detail,
                        source_ref = source_ref
                    )
                )

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

        exception_types = buildNode( provider, exception_expression, source_ref, True )

        if exception_types is None:
            exception_types = ()
        elif exception_types.isExpressionMakeSequence():
            exception_types = exception_types.getElements()
        else:
            exception_types = ( exception_types, )

        handlers.append(
            StatementExceptHandler(
                exception_types = exception_types,
                body            = handler_body,
                source_ref      = source_ref
            )
        )

    tried = buildStatementsNode(
        provider   = provider,
        nodes      = node.body,
        source_ref = source_ref
    )

    no_raise = buildStatementsNode(
        provider   = provider,
        nodes      = node.orelse,
        source_ref = source_ref
    )

    if no_raise is None:
        return StatementTryExcept(
            handlers   = handlers,
            tried      = tried,
            source_ref = source_ref
        )
    else:
        return makeTryExceptNoRaise(
            handlers   = handlers,
            tried      = tried,
            no_raise   = no_raise,
            source_ref = source_ref
        )