Refactor evaluate_augassign and test all operators (#313)
* Test all augassign operators * Refactor evaluate_augassign
This commit is contained in:
		
							parent
							
								
									6196958deb
								
							
						
					
					
						commit
						0ead477263
					
				|  | @ -369,84 +369,45 @@ def evaluate_augassign( | ||||||
|         if isinstance(current_value, list): |         if isinstance(current_value, list): | ||||||
|             if not isinstance(value_to_add, list): |             if not isinstance(value_to_add, list): | ||||||
|                 raise InterpreterError(f"Cannot add non-list value {value_to_add} to a list.") |                 raise InterpreterError(f"Cannot add non-list value {value_to_add} to a list.") | ||||||
|             try: |             current_value += value_to_add | ||||||
|                 updated_value = current_value.__iadd__(value_to_add) |  | ||||||
|             except AttributeError: |  | ||||||
|                 updated_value = current_value + value_to_add |  | ||||||
|         else: |         else: | ||||||
|             try: |             current_value += value_to_add | ||||||
|                 updated_value = current_value.__iadd__(value_to_add) |  | ||||||
|             except AttributeError: |  | ||||||
|                 updated_value = current_value + value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.Sub): |     elif isinstance(expression.op, ast.Sub): | ||||||
|         try: |         current_value -= value_to_add | ||||||
|             updated_value = current_value.__isub__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value - value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.Mult): |     elif isinstance(expression.op, ast.Mult): | ||||||
|         try: |         current_value *= value_to_add | ||||||
|             updated_value = current_value.__imul__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value * value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.Div): |     elif isinstance(expression.op, ast.Div): | ||||||
|         try: |         current_value /= value_to_add | ||||||
|             updated_value = current_value.__itruediv__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value / value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.Mod): |     elif isinstance(expression.op, ast.Mod): | ||||||
|         try: |         current_value %= value_to_add | ||||||
|             updated_value = current_value.__imod__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value % value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.Pow): |     elif isinstance(expression.op, ast.Pow): | ||||||
|         try: |         current_value **= value_to_add | ||||||
|             updated_value = current_value.__ipow__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value**value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.FloorDiv): |     elif isinstance(expression.op, ast.FloorDiv): | ||||||
|         try: |         current_value //= value_to_add | ||||||
|             updated_value = current_value.__ifloordiv__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value // value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.BitAnd): |     elif isinstance(expression.op, ast.BitAnd): | ||||||
|         try: |         current_value &= value_to_add | ||||||
|             updated_value = current_value.__iand__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value & value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.BitOr): |     elif isinstance(expression.op, ast.BitOr): | ||||||
|         try: |         current_value |= value_to_add | ||||||
|             updated_value = current_value.__ior__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value | value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.BitXor): |     elif isinstance(expression.op, ast.BitXor): | ||||||
|         try: |         current_value ^= value_to_add | ||||||
|             updated_value = current_value.__ixor__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value ^ value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.LShift): |     elif isinstance(expression.op, ast.LShift): | ||||||
|         try: |         current_value <<= value_to_add | ||||||
|             updated_value = current_value.__ilshift__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value << value_to_add |  | ||||||
|     elif isinstance(expression.op, ast.RShift): |     elif isinstance(expression.op, ast.RShift): | ||||||
|         try: |         current_value >>= value_to_add | ||||||
|             updated_value = current_value.__irshift__(value_to_add) |  | ||||||
|         except AttributeError: |  | ||||||
|             updated_value = current_value >> value_to_add |  | ||||||
|     else: |     else: | ||||||
|         raise InterpreterError(f"Operation {type(expression.op).__name__} is not supported.") |         raise InterpreterError(f"Operation {type(expression.op).__name__} is not supported.") | ||||||
| 
 | 
 | ||||||
|     # Update the state |     # Update the state: current_value has been updated in-place | ||||||
|     set_value( |     set_value( | ||||||
|         expression.target, |         expression.target, | ||||||
|         updated_value, |         current_value, | ||||||
|         state, |         state, | ||||||
|         static_tools, |         static_tools, | ||||||
|         custom_tools, |         custom_tools, | ||||||
|         authorized_imports, |         authorized_imports, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     return updated_value |     return current_value | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def evaluate_boolop( | def evaluate_boolop( | ||||||
|  |  | ||||||
|  | @ -951,42 +951,117 @@ texec(tcompile("1 + 1", "no filename", "exec")) | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestPythonInterpreter: | @pytest.mark.parametrize( | ||||||
|     @pytest.mark.parametrize( |     "code, expected_result", | ||||||
|         "code, expected_result", |     [ | ||||||
|         [ |         ( | ||||||
|             ( |             dedent("""\ | ||||||
|                 dedent("""\ |                 x = 1 | ||||||
|                     x = 1 |                 x += 2 | ||||||
|                     x += 2 |             """), | ||||||
|                 """), |             3, | ||||||
|                 3, |         ), | ||||||
|             ), |         ( | ||||||
|             ( |             dedent("""\ | ||||||
|                 dedent("""\ |                 x = "a" | ||||||
|                     x = "a" |                 x += "b" | ||||||
|                     x += "b" |             """), | ||||||
|                 """), |             "ab", | ||||||
|                 "ab", |         ), | ||||||
|             ), |         ( | ||||||
|             ( |             dedent("""\ | ||||||
|                 dedent("""\ |                 class Custom: | ||||||
|                     class Custom: |                     def __init__(self, value): | ||||||
|                         def __init__(self, value): |                         self.value = value | ||||||
|                             self.value = value |                     def __iadd__(self, other): | ||||||
|                         def __iadd__(self, other): |                         self.value += other * 10 | ||||||
|                             self.value += other * 10 |                         return self | ||||||
|                             return self |  | ||||||
| 
 | 
 | ||||||
|                     x = Custom(1) |                 x = Custom(1) | ||||||
|                     x += 2 |                 x += 2 | ||||||
|                     x.value |                 x.value | ||||||
|                 """), |             """), | ||||||
|                 21, |             21, | ||||||
|             ), |         ), | ||||||
|         ], |     ], | ||||||
|     ) | ) | ||||||
|     def test_evaluate_augassign(self, code, expected_result): | def test_evaluate_augassign(code, expected_result): | ||||||
|         state = {} |     state = {} | ||||||
|         result, _ = evaluate_python_code(code, {}, state=state) |     result, _ = evaluate_python_code(code, {}, state=state) | ||||||
|         assert result == expected_result |     assert result == expected_result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.parametrize( | ||||||
|  |     "operator, expected_result", | ||||||
|  |     [ | ||||||
|  |         ("+=", 7), | ||||||
|  |         ("-=", 3), | ||||||
|  |         ("*=", 10), | ||||||
|  |         ("/=", 2.5), | ||||||
|  |         ("//=", 2), | ||||||
|  |         ("%=", 1), | ||||||
|  |         ("**=", 25), | ||||||
|  |         ("&=", 0), | ||||||
|  |         ("|=", 7), | ||||||
|  |         ("^=", 7), | ||||||
|  |         (">>=", 1), | ||||||
|  |         ("<<=", 20), | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | def test_evaluate_augassign_number(operator, expected_result): | ||||||
|  |     code = dedent("""\ | ||||||
|  |         x = 5 | ||||||
|  |         x {operator} 2 | ||||||
|  |     """).format(operator=operator) | ||||||
|  |     state = {} | ||||||
|  |     result, _ = evaluate_python_code(code, {}, state=state) | ||||||
|  |     assert result == expected_result | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.parametrize( | ||||||
|  |     "operator, expected_result", | ||||||
|  |     [ | ||||||
|  |         ("+=", 7), | ||||||
|  |         ("-=", 3), | ||||||
|  |         ("*=", 10), | ||||||
|  |         ("/=", 2.5), | ||||||
|  |         ("//=", 2), | ||||||
|  |         ("%=", 1), | ||||||
|  |         ("**=", 25), | ||||||
|  |         ("&=", 0), | ||||||
|  |         ("|=", 7), | ||||||
|  |         ("^=", 7), | ||||||
|  |         (">>=", 1), | ||||||
|  |         ("<<=", 20), | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | def test_evaluate_augassign_custom(operator, expected_result): | ||||||
|  |     operator_names = { | ||||||
|  |         "+=": "iadd", | ||||||
|  |         "-=": "isub", | ||||||
|  |         "*=": "imul", | ||||||
|  |         "/=": "itruediv", | ||||||
|  |         "//=": "ifloordiv", | ||||||
|  |         "%=": "imod", | ||||||
|  |         "**=": "ipow", | ||||||
|  |         "&=": "iand", | ||||||
|  |         "|=": "ior", | ||||||
|  |         "^=": "ixor", | ||||||
|  |         ">>=": "irshift", | ||||||
|  |         "<<=": "ilshift", | ||||||
|  |     } | ||||||
|  |     code = dedent("""\ | ||||||
|  |         class Custom: | ||||||
|  |             def __init__(self, value): | ||||||
|  |                 self.value = value | ||||||
|  |             def __{operator_name}__(self, other): | ||||||
|  |                 self.value {operator} other | ||||||
|  |                 return self | ||||||
|  | 
 | ||||||
|  |         x = Custom(5) | ||||||
|  |         x {operator} 2 | ||||||
|  |         x.value | ||||||
|  |     """).format(operator=operator, operator_name=operator_names[operator]) | ||||||
|  |     state = {} | ||||||
|  |     result, _ = evaluate_python_code(code, {}, state=state) | ||||||
|  |     assert result == expected_result | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue