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)
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, )