test_exceptions.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import pytest
  2. from pybind11_tests import exceptions as m
  3. import pybind11_cross_module_tests as cm
  4. def test_std_exception(msg):
  5. with pytest.raises(RuntimeError) as excinfo:
  6. m.throw_std_exception()
  7. assert msg(excinfo.value) == "This exception was intentionally thrown."
  8. def test_error_already_set(msg):
  9. with pytest.raises(RuntimeError) as excinfo:
  10. m.throw_already_set(False)
  11. assert msg(excinfo.value) == "Unknown internal error occurred"
  12. with pytest.raises(ValueError) as excinfo:
  13. m.throw_already_set(True)
  14. assert msg(excinfo.value) == "foo"
  15. def test_cross_module_exceptions():
  16. with pytest.raises(RuntimeError) as excinfo:
  17. cm.raise_runtime_error()
  18. assert str(excinfo.value) == "My runtime error"
  19. with pytest.raises(ValueError) as excinfo:
  20. cm.raise_value_error()
  21. assert str(excinfo.value) == "My value error"
  22. with pytest.raises(ValueError) as excinfo:
  23. cm.throw_pybind_value_error()
  24. assert str(excinfo.value) == "pybind11 value error"
  25. with pytest.raises(TypeError) as excinfo:
  26. cm.throw_pybind_type_error()
  27. assert str(excinfo.value) == "pybind11 type error"
  28. with pytest.raises(StopIteration) as excinfo:
  29. cm.throw_stop_iteration()
  30. def test_python_call_in_catch():
  31. d = {}
  32. assert m.python_call_in_destructor(d) is True
  33. assert d["good"] is True
  34. def test_exception_matches():
  35. m.exception_matches()
  36. def test_custom(msg):
  37. # Can we catch a MyException?
  38. with pytest.raises(m.MyException) as excinfo:
  39. m.throws1()
  40. assert msg(excinfo.value) == "this error should go to a custom type"
  41. # Can we translate to standard Python exceptions?
  42. with pytest.raises(RuntimeError) as excinfo:
  43. m.throws2()
  44. assert msg(excinfo.value) == "this error should go to a standard Python exception"
  45. # Can we handle unknown exceptions?
  46. with pytest.raises(RuntimeError) as excinfo:
  47. m.throws3()
  48. assert msg(excinfo.value) == "Caught an unknown exception!"
  49. # Can we delegate to another handler by rethrowing?
  50. with pytest.raises(m.MyException) as excinfo:
  51. m.throws4()
  52. assert msg(excinfo.value) == "this error is rethrown"
  53. # Can we fall-through to the default handler?
  54. with pytest.raises(RuntimeError) as excinfo:
  55. m.throws_logic_error()
  56. assert msg(excinfo.value) == "this error should fall through to the standard handler"
  57. # Can we handle a helper-declared exception?
  58. with pytest.raises(m.MyException5) as excinfo:
  59. m.throws5()
  60. assert msg(excinfo.value) == "this is a helper-defined translated exception"
  61. # Exception subclassing:
  62. with pytest.raises(m.MyException5) as excinfo:
  63. m.throws5_1()
  64. assert msg(excinfo.value) == "MyException5 subclass"
  65. assert isinstance(excinfo.value, m.MyException5_1)
  66. with pytest.raises(m.MyException5_1) as excinfo:
  67. m.throws5_1()
  68. assert msg(excinfo.value) == "MyException5 subclass"
  69. with pytest.raises(m.MyException5) as excinfo:
  70. try:
  71. m.throws5()
  72. except m.MyException5_1:
  73. raise RuntimeError("Exception error: caught child from parent")
  74. assert msg(excinfo.value) == "this is a helper-defined translated exception"
  75. def test_nested_throws(capture):
  76. """Tests nested (e.g. C++ -> Python -> C++) exception handling"""
  77. def throw_myex():
  78. raise m.MyException("nested error")
  79. def throw_myex5():
  80. raise m.MyException5("nested error 5")
  81. # In the comments below, the exception is caught in the first step, thrown in the last step
  82. # C++ -> Python
  83. with capture:
  84. m.try_catch(m.MyException5, throw_myex5)
  85. assert str(capture).startswith("MyException5: nested error 5")
  86. # Python -> C++ -> Python
  87. with pytest.raises(m.MyException) as excinfo:
  88. m.try_catch(m.MyException5, throw_myex)
  89. assert str(excinfo.value) == "nested error"
  90. def pycatch(exctype, f, *args):
  91. try:
  92. f(*args)
  93. except m.MyException as e:
  94. print(e)
  95. # C++ -> Python -> C++ -> Python
  96. with capture:
  97. m.try_catch(
  98. m.MyException5, pycatch, m.MyException, m.try_catch, m.MyException, throw_myex5)
  99. assert str(capture).startswith("MyException5: nested error 5")
  100. # C++ -> Python -> C++
  101. with capture:
  102. m.try_catch(m.MyException, pycatch, m.MyException5, m.throws4)
  103. assert capture == "this error is rethrown"
  104. # Python -> C++ -> Python -> C++
  105. with pytest.raises(m.MyException5) as excinfo:
  106. m.try_catch(m.MyException, pycatch, m.MyException, m.throws5)
  107. assert str(excinfo.value) == "this is a helper-defined translated exception"