Files
2026-07-13 12:40:42 +08:00

979 lines
27 KiB
Python

# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import unittest
from test_case_base import (
TestCaseBase,
)
import paddle
from paddle.jit.sot import symbolic_translate
from paddle.jit.sot.opcode_translator.executor.opcode_executor import (
ALREADY_SUPPORTED_EXCEPTION,
)
from paddle.jit.sot.psdb import check_no_breakgraph
from paddle.jit.sot.utils import strict_mode_guard
NOT_ALLOW_FALLBACK = ALREADY_SUPPORTED_EXCEPTION
class TestRaiseVarargs(TestCaseBase):
# test `RAISE_VARARGS`
@staticmethod
@check_no_breakgraph
def argc_equal_to_0_wo_exception(x):
try:
x += 1
# In CPython, `RuntimeError` will be triggered at this point.
# SOT have setup the exception stack and raise the exception manually.
# This function is designed to test scenarios involving only the `raise` statement.
raise # Bare `raise` statement is not inside an exception handler # noqa: PLE0704
x /= 2
except RuntimeError:
x -= 3
x *= 4
return x
@staticmethod
@check_no_breakgraph
def argc_equal_to_0_zero_div_err(x):
x += 1
try:
try:
x += 2
result = 10 / 0
except ZeroDivisionError:
x += 3
raise # RAISE_VARARGS(0)
except:
x += 4
return x + 5
@staticmethod
@check_no_breakgraph
def argc_equal_to_0_simulating_zero_div_err(x):
x += 1
try:
try:
x += 2
raise ZeroDivisionError("")
except ZeroDivisionError:
x += 3
raise # RAISE_VARARGS(0)
except:
x += 4
return x + 5
@staticmethod
@check_no_breakgraph
def argc_equal_to_1(x):
x += 1
try:
try:
x += 2
except:
x += 3
else:
x += 4
raise NotImplementedError # RAISE_VARARGS(1)
x += 5
except:
x += 6
return x + 7
@staticmethod
@check_no_breakgraph
def argc_equal_to_2(x):
x -= 10
try:
try:
x -= 300
finally:
x -= 400
raise ValueError from None # RAISE_VARARGS(2)
except ValueError:
x -= 500
return x - 600
@staticmethod
@check_no_breakgraph
def argc_equal_to_1_2(x):
try:
x += 1
try:
x /= 2
raise NameError # RAISE_VARARGS(1)
x *= 3
except NameError as e:
x -= 4
raise TimeoutError("TESTING") from e # RAISE_VARARGS(2)
except:
x /= 5
return x + 6
@staticmethod
@check_no_breakgraph
def argc_equal_to_1_0(x):
try:
try:
x -= 1
raise ValueError("TESTING") # RAISE_VARARGS(1)
x += 2
except NotImplementedError:
x /= 3
raise # RAISE_VARARGS(0)
except (KeyError, IndexError):
x *= 4
except ValueError:
x += 5
return x
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_RAISE_VARARGS_argc(self):
self.assert_results(
self.argc_equal_to_0_wo_exception, paddle.to_tensor(0.01)
)
self.assert_results(
self.argc_equal_to_0_zero_div_err, paddle.to_tensor(0.02)
)
self.assert_results(
self.argc_equal_to_0_simulating_zero_div_err, paddle.to_tensor(0.03)
)
self.assert_results(self.argc_equal_to_1, paddle.to_tensor(0.04))
self.assert_results(self.argc_equal_to_2, paddle.to_tensor(0.05))
self.assert_results(self.argc_equal_to_1_2, paddle.to_tensor(0.06))
self.assert_results(self.argc_equal_to_1_0, paddle.to_tensor(0.07))
class TestException(TestCaseBase):
@staticmethod
def create_builtin_exception(x):
def identity(e):
return e
x += 1
value_error = ValueError()
identity(value_error)
x += 2
type_error = TypeError("")
identity(type_error)
x += 3
key_error = KeyError("")
identity(key_error)
x += 4
exception = Exception("")
identity(exception)
x += 5
unicode_translate_error = UnicodeTranslateError("", -1, -1, "")
identity(unicode_translate_error)
x += 6
return x
@staticmethod
def create_user_defined_exception(x):
# TODO: Need to support user-defined exception
return x
@check_no_breakgraph
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_dispatch(self):
self.assert_results(
self.create_builtin_exception, paddle.to_tensor(111.0)
)
self.assert_results(
self.create_user_defined_exception, paddle.to_tensor(222.0)
)
class TestTryExcept(TestCaseBase):
# try ... except ...
# ---------------- test raising exception directly ----------------
@staticmethod
def raise_value_error_obj():
raise ValueError("Test whether raising `ValueError`")
@staticmethod
def raise_value_error_cls():
raise ValueError
@staticmethod
def raise_asserterror(x):
assert x, "Test AssertionError"
# Since the exceptions are not handled, fallback is permitted.
@strict_mode_guard(False)
def test_exception_raising(self):
with self.assertRaisesRegex(
ValueError, "Test whether raising `ValueError`"
):
symbolic_translate(self.raise_value_error_obj)()
with self.assertRaisesRegex(ValueError, ""):
symbolic_translate(self.raise_value_error_cls)()
with self.assertRaisesRegex(AssertionError, "Test AssertionError"):
symbolic_translate(self.raise_asserterror)(False)
with self.assertRaisesRegex(AssertionError, "Test AssertionError"):
symbolic_translate(self.raise_asserterror)(paddle.to_tensor(0))
# ---------------- without error ----------------
@staticmethod
@check_no_breakgraph
def try_except_wo_error(x):
try:
x = x + 1
except:
x = x * 2
return x
@staticmethod
@check_no_breakgraph
def try_except_exception_wo_error(x):
try:
x = x + 1
except Exception:
x = x * 2
return x
@staticmethod
@check_no_breakgraph
def try_except_exception_as_e_wo_error(x):
try:
x = x + 1
except Exception as e:
x = x * 2
return x
# ---------------- with error ----------------
@staticmethod
@check_no_breakgraph
def try_except_with_error_obj(x):
y = x + 3
try:
x = x + 1
raise ValueError(f"{__class__.__name__}")
x = x * 3
except:
y = x * 2
return y
@staticmethod
@check_no_breakgraph
def try_except_with_error_cls(x):
y = x + 3
try:
x = x + 1
raise ValueError
x = x * 3
except:
y = x * 2
return y
@staticmethod
@check_no_breakgraph
def try_except_exception_with_error(x):
# test `JUMP_IF_NOT_EXC_MATCH`
x = x + 3
try:
x = x + 1
raise ValueError("TESTING!")
x = x * 3
except Exception:
x = x * 2
return x
@staticmethod
@check_no_breakgraph
def try_except_exception_as_e_with_error(x):
# test `JUMP_IF_NOT_EXC_MATCH`
y = x + 3
try:
x = x + 1
raise ValueError("TESTING!")
x = x * 3
except ValueError as e:
y = x * 2
return y
@staticmethod
@check_no_breakgraph
def try_except_exception_as_e_with_error_tuple(x):
# test `JUMP_IF_NOT_EXC_MATCH`
y = x + 3
try:
x = x + 1
raise ValueError("TESTING!")
x = x * 3
except (ValueError, KeyError, NotImplementedError) as e:
y = x * 2
return y
@staticmethod
@check_no_breakgraph
def try_except_exception_as_e_with_unmatched_error(x):
# test `JUMP_IF_NOT_EXC_MATCH`
y = x + 3
try:
x = x + 1
raise ValueError("TESTING!")
x = x * 3
except KeyError as e:
y = x * 2
return y + 3
@staticmethod
@check_no_breakgraph
def try_except_exception_as_e_with_matched_error_reraise(x):
# test `JUMP_IF_NOT_EXC_MATCH`
y = x + 3
try:
x = x + 1
raise IndexError("TESTING!")
x = x * 3
except IndexError as e:
y = x * 2
raise LookupError("TESTING!")
return y + 3
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_try_except(self):
self.assert_results(self.try_except_wo_error, paddle.to_tensor(2))
self.assert_results(
self.try_except_exception_wo_error, paddle.to_tensor(3)
)
self.assert_results(
self.try_except_exception_as_e_wo_error, paddle.to_tensor(4)
)
self.assert_results(self.try_except_with_error_obj, paddle.to_tensor(5))
self.assert_results(self.try_except_with_error_cls, paddle.to_tensor(6))
self.assert_results(
self.try_except_exception_with_error, paddle.to_tensor(7)
)
self.assert_results(
self.try_except_exception_as_e_with_error, paddle.to_tensor(8)
)
self.assert_results(
self.try_except_exception_as_e_with_error_tuple, paddle.to_tensor(9)
)
@strict_mode_guard(False)
def test_error(self):
# RERAISE
self.assert_exceptions(
ValueError,
"TESTING!",
self.try_except_exception_as_e_with_unmatched_error,
paddle.to_tensor(0.001),
)
self.assert_exceptions(
LookupError,
"TESTING!",
self.try_except_exception_as_e_with_matched_error_reraise,
paddle.to_tensor(0.001),
)
class TestTryFinally(TestCaseBase):
# try ... finally ...
# ---------------- without error ----------------
@staticmethod
@check_no_breakgraph
def try_finally_wo_error(x):
try:
x = 1 + x
finally:
x *= 2
return x
# ---------------- with error ----------------
@staticmethod
@check_no_breakgraph
def try_finally_with_error_but_return_in_finally(x):
# RERAISE
try:
x = 3 + x
raise NotImplementedError("TESTING!")
x = 300 + x
finally:
x *= 2
# `return` inside `finally` blocks cause exceptions to be silenced
return x # noqa: B012
@staticmethod
def try_finally_with_error(x):
# RERAISE
try:
x = 3 + x
raise NotImplementedError("TESTING!")
x = 300 + x
finally:
x *= 2
return x
@staticmethod
def try_finally_with_error_in_finally(x):
# RERAISE
try:
x = 3 + x
return x
finally:
x *= 2
raise TimeoutError("TESTING!")
x = 300 + x
return x
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_try_finally(self):
self.assert_results(self.try_finally_wo_error, paddle.to_tensor(14))
self.assert_results(
self.try_finally_with_error_but_return_in_finally,
paddle.to_tensor(15),
)
@strict_mode_guard(False)
def test_error(self):
# RERAISE
self.assert_exceptions(
NotImplementedError,
"TESTING!",
self.try_finally_with_error,
paddle.to_tensor(16),
)
self.assert_exceptions(
TimeoutError,
"TESTING!",
self.try_finally_with_error_in_finally,
paddle.to_tensor(17),
)
class TestTryExceptElse(TestCaseBase):
# try ... except ... else
# `else` is useful for code that must be executed if the try clause does not raise an exception.
# ---------------- without error ----------------
@staticmethod
def try_except_else(x):
try:
x += 1
except:
x += 2
else:
x += 3
return x
# ---------------- with error ----------------
@staticmethod
def try_except_else_except_with_matched_error(x):
try:
x += 4
raise ValueError
except ValueError:
x += 5
else:
x += 6
return x
@staticmethod
@strict_mode_guard(False)
def try_except_else_except_with_mismatched_error(x):
try:
x += 4
raise TimeoutError
except KeyError:
x += 5
else:
x += 6
return x
@staticmethod
def try_except_else_error_in_except(x):
try:
x += 4
except KeyError:
x += 5
raise ValueError
else:
x += 6
return x
@staticmethod
@strict_mode_guard(False)
def try_except_else_error_in_else(x):
try:
x += 4
except KeyError:
x += 5
else:
x += 6
raise ValueError("Testing!")
return x
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_try_except_else(self):
# self.assert_results(self.try_except_else, paddle.to_tensor(14))
self.assert_results(
self.try_except_else_except_with_matched_error, paddle.to_tensor(15)
)
# self.assert_results(
# self.try_except_else_error_in_except, paddle.to_tensor(16)
# )
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_error(self):
# RERAISE
self.assert_exceptions(
TimeoutError,
"",
self.try_except_else_except_with_mismatched_error,
paddle.to_tensor(0.001),
)
self.assert_exceptions(
ValueError,
"Testing!",
self.try_except_else_error_in_else,
paddle.to_tensor(0.002),
)
class TestTryExceptFinally(TestCaseBase):
# try ... except ... finally
# ---------------- without error ----------------
@staticmethod
def try_except_finally(x):
try:
x -= 1
except:
x -= 2
finally:
x -= 3
return x
# ---------------- without error ----------------
@staticmethod
def try_except_finally_with_matched_exception(x):
try:
x -= 1
raise ValueError
except:
x -= 2
finally:
x -= 3
return x
@staticmethod
@strict_mode_guard(False)
def try_except_finally_with_mismatched_exception(x):
try:
x -= 1
raise ValueError("TESTING")
except AttributeError:
x -= 2
finally:
x -= 3
return x
@staticmethod
def try_except_finally_in_except(x):
try:
x -= 1
except AttributeError:
x -= 2
raise ValueError
finally:
x -= 3
return x
@staticmethod
@strict_mode_guard(False)
def try_except_finally_in_finally(x):
try:
x -= 1
except AttributeError:
x -= 2
finally:
x -= 3
raise ValueError
return x
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_try_except_finally(self):
self.assert_results(self.try_except_finally, paddle.to_tensor([0.11]))
self.assert_results(
self.try_except_finally_with_matched_exception,
paddle.to_tensor([0.22]),
)
self.assert_results(
self.try_except_finally_in_except,
paddle.to_tensor([0.33]),
)
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_error(self):
# RERAISE
self.assert_exceptions(
ValueError,
"TESTING",
self.try_except_finally_with_mismatched_exception,
paddle.to_tensor(0.001),
)
self.assert_exceptions(
ValueError,
"",
self.try_except_finally_in_finally,
paddle.to_tensor(0.001),
)
class TestTryExceptElseFinally(TestCaseBase):
# try ... except ... else ... finally
# ---------------- without error ----------------
@staticmethod
def try_except_else_finally(x):
try:
x -= 1
except:
x -= 2
else:
x -= 3
finally:
x -= 4
return x
# ---------------- with error ----------------
@staticmethod
def try_except_else_finally_with_matched_exception(x):
try:
x -= 1
raise ValueError
except ValueError:
x -= 2
else:
x -= 3
finally:
x -= 4
return x
@staticmethod
@strict_mode_guard(False)
def try_except_else_finally_with_mismatched_exception(x):
try:
x -= 1
raise SystemError("TESTING")
except NotImplementedError:
x -= 2
else:
x -= 3
finally:
x -= 4
return x
@staticmethod
@strict_mode_guard(False)
def try_except_else_finally_with_exception_in_else(x):
try:
x -= 1
except NotImplementedError:
x -= 2
else:
x -= 3
raise ModuleNotFoundError("TESTING")
finally:
x -= 4
return x
@staticmethod
@strict_mode_guard(False)
def try_except_else_finally_with_exception_in_finally(x):
try:
x -= 1
except NotImplementedError:
x -= 2
else:
x -= 3
finally:
x -= 4
raise SyntaxError("TESTING")
return x
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_try_except_finally(self):
self.assert_results(
self.try_except_else_finally, paddle.to_tensor([0.11])
)
self.assert_results(
self.try_except_else_finally_with_matched_exception,
paddle.to_tensor([0.22]),
)
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_error(self):
# RERAISE
self.assert_exceptions(
SystemError,
"TESTING",
self.try_except_else_finally_with_mismatched_exception,
paddle.to_tensor(0.001),
)
self.assert_exceptions(
ModuleNotFoundError,
"TESTING",
self.try_except_else_finally_with_exception_in_else,
paddle.to_tensor(0.001),
)
self.assert_exceptions(
SyntaxError,
"TESTING",
self.try_except_else_finally_with_exception_in_finally,
paddle.to_tensor(0.001),
)
class TestNestingCase(TestCaseBase):
@strict_mode_guard(NOT_ALLOW_FALLBACK)
@check_no_breakgraph
def test_try_nesting(self):
def try_nesting_wo_error(x):
try:
try:
try:
try:
try:
try:
x -= 1
raise ValueError(
"TESTING"
) # RAISE_VARARGS(1)
x += 2
except NotImplementedError:
x /= 3
raise # RAISE_VARARGS(0)
except (KeyError, IndexError):
x *= 4
except ValueError:
x += 5
raise NameError # RAISE_VARARGS(1)
except SyntaxError:
x /= 6
except (TypeError, FileNotFoundError, NameError) as e:
x -= 7
raise TimeoutError(
"TESTING"
) from e # RAISE_VARARGS(2)
except:
x /= 8
raise AssertionError
except IndentationError as e:
x *= 9
except AssertionError as e:
x += 10
raise # RAISE_VARARGS(0)
except Exception as e:
x /= 11
return x + 12
self.assert_results(try_nesting_wo_error, paddle.to_tensor(0.5))
@strict_mode_guard(NOT_ALLOW_FALLBACK)
@check_no_breakgraph
def test_function_nesting(self):
def raise_value_error_obj(x):
x += 1
raise ValueError("")
def raise_value_error_cls(x):
x += 2
raise ValueError
def raise_zero_div_error(x):
x += 3
return 19.0 / 0
def raise_assert_error(x):
x += 4
assert []
def raise_not_implemented_error(x):
x += 5
raise NotImplementedError
def one_nesting(x, func):
x *= 6
func(x)
def two_nesting(x, func):
x /= 7
one_nesting(x, func)
def three_nesting(x, func):
x -= 8
two_nesting(x, func)
def get_test_func(x, func=None):
try:
x += 1
try:
x /= 2
three_nesting(x, func)
x -= 3
except ValueError:
x *= 4
except:
x += 5
return x # / 6
self.assert_results(
get_test_func, paddle.to_tensor(0.3), raise_value_error_obj
)
self.assert_results(
get_test_func, paddle.to_tensor(0.4), raise_value_error_cls
)
self.assert_results(
get_test_func, paddle.to_tensor(0.5), raise_zero_div_error
)
self.assert_results(
get_test_func, paddle.to_tensor(0.6), raise_assert_error
)
self.assert_results(
get_test_func, paddle.to_tensor(0.7), raise_not_implemented_error
)
class TestAssertException(TestCaseBase):
@staticmethod
def try_assert(x, condition):
# test py value or paddle tensor value as condition
try:
x += 1
try:
x /= 2
raise TimeoutError("TESTING")
except:
x -= 3
assert condition
except:
x *= 4
return x / 5
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_assert_with_py_var_as_condition(self):
# Test the case where `condition` is Python variable
self.assert_results(self.try_assert, paddle.to_tensor(1), False)
self.assert_results(self.try_assert, paddle.to_tensor(2), True)
self.assert_results(self.try_assert, paddle.to_tensor(3), [])
self.assert_results(self.try_assert, paddle.to_tensor(4), [1])
self.assert_results(self.try_assert, paddle.to_tensor(5), "")
self.assert_results(self.try_assert, paddle.to_tensor(6), "QAQ")
# TODO(DrRyanHuang): The following two cases are not supported yet.
# self.assert_results(self.try_assert, paddle.to_tensor(7), ValueError)
# self.assert_results(self.try_assert, paddle.to_tensor(8), ValueError())
# Currently, since the assert statement is essentially an if statement and can cause breakgraph,
# using a Tensor as a condition is not supported. Therefore, fallback is allowed.
@strict_mode_guard(False)
def test_assert_with_tensor_as_condition(self):
# Test the case where `condition` is Paddle Tensor
self.assert_results(
self.try_assert, paddle.to_tensor(8), paddle.to_tensor(1)
)
self.assert_results(
self.try_assert, paddle.to_tensor(9), paddle.to_tensor(0)
)
self.assert_results(
self.try_assert, paddle.to_tensor(10), paddle.to_tensor(-1)
)
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_assert_true(self):
@check_no_breakgraph
def try_assert_except(x, y):
x += 1
try:
x += 2
assert y > -10000
x += 3
except:
x += 4
self.assert_results(try_assert_except, paddle.to_tensor(10), 10)
@strict_mode_guard(NOT_ALLOW_FALLBACK)
def test_assert_false(self):
@check_no_breakgraph
def try_assert_except(x, y):
try:
x += 5
assert y < -10000
except AssertionError:
x += 6
return x
self.assert_results(try_assert_except, paddle.to_tensor(10), 10)
class TestGuard(TestCaseBase):
@strict_mode_guard(False)
@check_no_breakgraph
def test_guard_run(self):
def fn():
try:
paddle.jit.sot.psdb.breakgraph()
raise ValueError
except ValueError:
return True
return False
self.assert_results(fn)
class TestBuiltinFunctionRaiseExceptionGuard(TestCaseBase):
@strict_mode_guard(False)
def test_guard_run(self):
def foo_floordiv(x):
1 / x
def foo_mod(x):
2 % x
self.assert_results(foo_floordiv, 1)
self.assert_exceptions(
ZeroDivisionError,
"division by zero",
foo_floordiv,
0,
)
self.assert_results(foo_mod, 10)
if sys.version_info >= (3, 14):
zero_mod_msg = "division by zero"
else:
zero_mod_msg = "integer (.)*modulo by zero"
self.assert_exceptions(
ZeroDivisionError,
zero_mod_msg,
foo_mod,
0,
)
if __name__ == "__main__":
unittest.main()