1# Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com 2# 3# Part of "Nuitka", an optimizing Python compiler that is compatible and 4# integrates with CPython, but also works on its own. 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18""" Print nodes. 19 20Right now there is only the print statement, but in principle, there should 21also be the print function here. These perform output, which can be combined 22if possible, and could be detected to fail, which would be perfect. 23 24Predicting the behavior of 'print' is not trivial at all, due to many special 25cases. 26""" 27 28from .NodeBases import StatementChildHavingBase, StatementChildrenHavingBase 29from .NodeMakingHelpers import ( 30 makeStatementExpressionOnlyReplacementNode, 31 makeStatementsSequenceReplacementNode, 32 wrapStatementWithSideEffects, 33) 34 35 36class StatementPrintValue(StatementChildrenHavingBase): 37 kind = "STATEMENT_PRINT_VALUE" 38 39 named_children = ("dest", "value") 40 41 def __init__(self, dest, value, source_ref): 42 StatementChildrenHavingBase.__init__( 43 self, values={"value": value, "dest": dest}, source_ref=source_ref 44 ) 45 46 assert value is not None 47 48 def computeStatement(self, trace_collection): 49 dest = trace_collection.onExpression( 50 expression=self.subnode_dest, allow_none=True 51 ) 52 53 if dest is not None and dest.mayRaiseException(BaseException): 54 trace_collection.onExceptionRaiseExit(BaseException) 55 56 if dest is not None and dest.willRaiseException(BaseException): 57 result = makeStatementExpressionOnlyReplacementNode( 58 expression=dest, node=self 59 ) 60 61 return ( 62 result, 63 "new_raise", 64 """\ 65Exception raise in 'print' statement destination converted to explicit raise.""", 66 ) 67 68 value = trace_collection.onExpression(expression=self.subnode_value) 69 70 if value.mayRaiseException(BaseException): 71 trace_collection.onExceptionRaiseExit(BaseException) 72 73 if value.willRaiseException(BaseException): 74 if dest is not None: 75 result = wrapStatementWithSideEffects( 76 new_node=makeStatementExpressionOnlyReplacementNode( 77 expression=value, node=self 78 ), 79 old_node=dest, 80 ) 81 else: 82 result = makeStatementExpressionOnlyReplacementNode( 83 expression=value, node=self 84 ) 85 86 return ( 87 result, 88 "new_raise", 89 """\ 90Exception raise in 'print' statement arguments converted to explicit raise.""", 91 ) 92 93 trace_collection.onExceptionRaiseExit(BaseException) 94 95 if dest is None: 96 if value.isExpressionSideEffects(): 97 self.setChild("value", value.subnode_expression) 98 99 statements = [ 100 makeStatementExpressionOnlyReplacementNode(side_effect, self) 101 for side_effect in value.subnode_side_effects 102 ] 103 104 statements.append(self) 105 106 result = makeStatementsSequenceReplacementNode( 107 statements=statements, node=self 108 ) 109 110 return ( 111 result, 112 "new_statements", 113 """\ 114Side effects printed item promoted to statements.""", 115 ) 116 117 if value.isCompileTimeConstant(): 118 # Avoid unicode encoding issues. 119 if not value.isExpressionConstantUnicodeRef(): 120 new_value = value.getStrValue() 121 assert new_value is not None, value 122 123 if value is not new_value: 124 self.setChild("value", new_value) 125 126 return self, None, None 127 128 @staticmethod 129 def mayRaiseException(exception_type): 130 # Output may always fail due to external reasons. 131 return True 132 133 134class StatementPrintNewline(StatementChildHavingBase): 135 kind = "STATEMENT_PRINT_NEWLINE" 136 137 named_child = "dest" 138 139 def __init__(self, dest, source_ref): 140 StatementChildHavingBase.__init__(self, value=dest, source_ref=source_ref) 141 142 def computeStatement(self, trace_collection): 143 dest = trace_collection.onExpression( 144 expression=self.subnode_dest, allow_none=True 145 ) 146 147 if dest is not None and dest.mayRaiseException(BaseException): 148 trace_collection.onExceptionRaiseExit(BaseException) 149 150 if dest is not None and dest.willRaiseException(BaseException): 151 result = makeStatementExpressionOnlyReplacementNode( 152 expression=dest, node=self 153 ) 154 155 return ( 156 result, 157 "new_raise", 158 """\ 159Exception raise in 'print' statement destination converted to explicit raise.""", 160 ) 161 162 trace_collection.onExceptionRaiseExit(BaseException) 163 164 return self, None, None 165 166 @staticmethod 167 def mayRaiseException(exception_type): 168 # Output may always fail due to external reasons. 169 return True 170