test_exceptions.py
Go to the documentation of this file.
1 from __future__ import annotations
2 
3 import sys
4 
5 import pytest
6 
7 import env
8 import pybind11_cross_module_tests as cm
9 import pybind11_tests
10 from pybind11_tests import exceptions as m
11 
12 
14  with pytest.raises(RuntimeError) as excinfo:
15  m.throw_std_exception()
16  assert msg(excinfo.value) == "This exception was intentionally thrown."
17 
18 
20  with pytest.raises(RuntimeError) as excinfo:
21  m.throw_already_set(False)
22  assert (
23  msg(excinfo.value)
24  == "Internal error: pybind11::error_already_set called while Python error indicator not set."
25  )
26 
27  with pytest.raises(ValueError) as excinfo:
28  m.throw_already_set(True)
29  assert msg(excinfo.value) == "foo"
30 
31 
32 def test_raise_from(msg):
33  with pytest.raises(ValueError) as excinfo:
34  m.raise_from()
35  assert msg(excinfo.value) == "outer"
36  assert msg(excinfo.value.__cause__) == "inner"
37 
38 
40  with pytest.raises(ValueError) as excinfo:
41  m.raise_from_already_set()
42  assert msg(excinfo.value) == "outer"
43  assert msg(excinfo.value.__cause__) == "inner"
44 
45 
47  with pytest.raises(RuntimeError) as excinfo:
48  cm.raise_runtime_error()
49  assert str(excinfo.value) == "My runtime error"
50 
51  with pytest.raises(ValueError) as excinfo:
52  cm.raise_value_error()
53  assert str(excinfo.value) == "My value error"
54 
55  with pytest.raises(ValueError) as excinfo:
56  cm.throw_pybind_value_error()
57  assert str(excinfo.value) == "pybind11 value error"
58 
59  with pytest.raises(TypeError) as excinfo:
60  cm.throw_pybind_type_error()
61  assert str(excinfo.value) == "pybind11 type error"
62 
63  with pytest.raises(StopIteration) as excinfo:
64  cm.throw_stop_iteration()
65 
66  with pytest.raises(cm.LocalSimpleException) as excinfo:
67  cm.throw_local_simple_error()
68  assert msg(excinfo.value) == "external mod"
69 
70  with pytest.raises(KeyError) as excinfo:
71  cm.throw_local_error()
72  # KeyError is a repr of the key, so it has an extra set of quotes
73  assert str(excinfo.value) == "'just local'"
74 
75 
76 # TODO: FIXME
77 @pytest.mark.xfail(
78  "env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang'))",
79  raises=RuntimeError,
80  reason="See Issue #2847, PR #2999, PR #4324",
81 )
83  with pytest.raises(KeyError):
84  # translator registered in cross_module_tests
85  m.throw_should_be_translated_to_key_error()
86 
87 
89  d = {}
90  assert m.python_call_in_destructor(d) is True
91  assert d["good"] is True
92 
93 
95  unraisable = "PytestUnraisableExceptionWarning"
96  if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6
97  dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
98  return dec(f)
99  return f
100 
101 
102 # TODO: find out why this fails on PyPy, https://foss.heptapod.net/pypy/pypy/-/issues/3583
103 @pytest.mark.xfail(env.PYPY, reason="Failure on PyPy 3.8 (7.3.7)", strict=False)
104 @ignore_pytest_unraisable_warning
105 def test_python_alreadyset_in_destructor(monkeypatch, capsys):
106  hooked = False
107  triggered = False
108 
109  if hasattr(sys, "unraisablehook"): # Python 3.8+
110  hooked = True
111  # Don't take `sys.unraisablehook`, as that's overwritten by pytest
112  default_hook = sys.__unraisablehook__
113 
114  def hook(unraisable_hook_args):
115  exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args
116  if obj == "already_set demo":
117  nonlocal triggered
118  triggered = True
119  default_hook(unraisable_hook_args)
120  return
121 
122  # Use monkeypatch so pytest can apply and remove the patch as appropriate
123  monkeypatch.setattr(sys, "unraisablehook", hook)
124 
125  assert m.python_alreadyset_in_destructor("already_set demo") is True
126  if hooked:
127  assert triggered is True
128 
129  _, captured_stderr = capsys.readouterr()
130  assert captured_stderr.startswith("Exception ignored in: 'already_set demo'")
131  assert captured_stderr.rstrip().endswith("KeyError: 'bar'")
132 
133 
135  assert m.exception_matches()
136  assert m.exception_matches_base()
137  assert m.modulenotfound_exception_matches_base()
138 
139 
140 def test_custom(msg):
141  # Can we catch a MyException?
142  with pytest.raises(m.MyException) as excinfo:
143  m.throws1()
144  assert msg(excinfo.value) == "this error should go to py::exception<MyException>"
145 
146  # Can we catch a MyExceptionUseDeprecatedOperatorCall?
147  with pytest.raises(m.MyExceptionUseDeprecatedOperatorCall) as excinfo:
148  m.throws1d()
149  assert (
150  msg(excinfo.value)
151  == "this error should go to py::exception<MyExceptionUseDeprecatedOperatorCall>"
152  )
153 
154  # Can we translate to standard Python exceptions?
155  with pytest.raises(RuntimeError) as excinfo:
156  m.throws2()
157  assert msg(excinfo.value) == "this error should go to a standard Python exception"
158 
159  # Can we handle unknown exceptions?
160  with pytest.raises(RuntimeError) as excinfo:
161  m.throws3()
162  assert msg(excinfo.value) == "Caught an unknown exception!"
163 
164  # Can we delegate to another handler by rethrowing?
165  with pytest.raises(m.MyException) as excinfo:
166  m.throws4()
167  assert msg(excinfo.value) == "this error is rethrown"
168 
169  # Can we fall-through to the default handler?
170  with pytest.raises(RuntimeError) as excinfo:
171  m.throws_logic_error()
172  assert (
173  msg(excinfo.value) == "this error should fall through to the standard handler"
174  )
175 
176  # OverFlow error translation.
177  with pytest.raises(OverflowError) as excinfo:
178  m.throws_overflow_error()
179 
180  # Can we handle a helper-declared exception?
181  with pytest.raises(m.MyException5) as excinfo:
182  m.throws5()
183  assert msg(excinfo.value) == "this is a helper-defined translated exception"
184 
185  # Exception subclassing:
186  with pytest.raises(m.MyException5) as excinfo:
187  m.throws5_1()
188  assert msg(excinfo.value) == "MyException5 subclass"
189  assert isinstance(excinfo.value, m.MyException5_1)
190 
191  with pytest.raises(m.MyException5_1) as excinfo:
192  m.throws5_1()
193  assert msg(excinfo.value) == "MyException5 subclass"
194 
195  with pytest.raises(m.MyException5) as excinfo: # noqa: PT012
196  try:
197  m.throws5()
198  except m.MyException5_1 as err:
199  raise RuntimeError("Exception error: caught child from parent") from err
200  assert msg(excinfo.value) == "this is a helper-defined translated exception"
201 
202 
203 def test_nested_throws(capture):
204  """Tests nested (e.g. C++ -> Python -> C++) exception handling"""
205 
206  def throw_myex():
207  raise m.MyException("nested error")
208 
209  def throw_myex5():
210  raise m.MyException5("nested error 5")
211 
212  # In the comments below, the exception is caught in the first step, thrown in the last step
213 
214  # C++ -> Python
215  with capture:
216  m.try_catch(m.MyException5, throw_myex5)
217  assert str(capture).startswith("MyException5: nested error 5")
218 
219  # Python -> C++ -> Python
220  with pytest.raises(m.MyException) as excinfo:
221  m.try_catch(m.MyException5, throw_myex)
222  assert str(excinfo.value) == "nested error"
223 
224  def pycatch(exctype, f, *args): # noqa: ARG001
225  try:
226  f(*args)
227  except m.MyException as e:
228  print(e)
229 
230  # C++ -> Python -> C++ -> Python
231  with capture:
232  m.try_catch(
233  m.MyException5,
234  pycatch,
235  m.MyException,
236  m.try_catch,
237  m.MyException,
238  throw_myex5,
239  )
240  assert str(capture).startswith("MyException5: nested error 5")
241 
242  # C++ -> Python -> C++
243  with capture:
244  m.try_catch(m.MyException, pycatch, m.MyException5, m.throws4)
245  assert capture == "this error is rethrown"
246 
247  # Python -> C++ -> Python -> C++
248  with pytest.raises(m.MyException5) as excinfo:
249  m.try_catch(m.MyException, pycatch, m.MyException, m.throws5)
250  assert str(excinfo.value) == "this is a helper-defined translated exception"
251 
252 
253 # TODO: Investigate this crash, see pybind/pybind11#5062 for background
254 @pytest.mark.skipif(
255  sys.platform.startswith("win32") and "Clang" in pybind11_tests.compiler_info,
256  reason="Started segfaulting February 2024",
257 )
259  with pytest.raises(RuntimeError) as excinfo:
260  m.throw_nested_exception()
261  assert str(excinfo.value) == "Outer Exception"
262  assert str(excinfo.value.__cause__) == "Inner Exception"
263 
264 
265 # This can often happen if you wrap a pybind11 class in a Python wrapper
267  class MyRepr:
268  def __repr__(self):
269  raise AttributeError("Example error")
270 
271  with pytest.raises(TypeError):
272  m.simple_bool_passthrough(MyRepr())
273 
274 
276  """Tests that a local translator works and that the local translator from
277  the cross module is not applied"""
278  with pytest.raises(RuntimeError) as excinfo:
279  m.throws6()
280  assert msg(excinfo.value) == "MyException6 only handled in this module"
281 
282  with pytest.raises(RuntimeError) as excinfo:
283  m.throws_local_error()
284  assert not isinstance(excinfo.value, KeyError)
285  assert msg(excinfo.value) == "never caught"
286 
287  with pytest.raises(Exception) as excinfo:
288  m.throws_local_simple_error()
289  assert not isinstance(excinfo.value, cm.LocalSimpleException)
290  assert msg(excinfo.value) == "this mod"
291 
292 
294  assert m.error_already_set_what(RuntimeError, "\ud927") == (
295  "RuntimeError: \\ud927",
296  False,
297  )
298 
299 
301  assert m.error_already_set_what(RuntimeError, b"\x80") == (
302  "RuntimeError: b'\\x80'",
303  False,
304  )
305 
306 
307 class FlakyException(Exception):
308  def __init__(self, failure_point):
309  if failure_point == "failure_point_init":
310  raise ValueError("triggered_failure_point_init")
311  self.failure_point = failure_point
312 
313  def __str__(self):
314  if self.failure_point == "failure_point_str":
315  raise ValueError("triggered_failure_point_str")
316  return "FlakyException.__str__"
317 
318 
319 @pytest.mark.parametrize(
320  ("exc_type", "exc_value", "expected_what"),
321  [
322  (ValueError, "plain_str", "ValueError: plain_str"),
323  (ValueError, ("tuple_elem",), "ValueError: tuple_elem"),
324  (FlakyException, ("happy",), "FlakyException: FlakyException.__str__"),
325  ],
326 )
328  exc_type, exc_value, expected_what
329 ):
330  what, py_err_set_after_what = m.error_already_set_what(exc_type, exc_value)
331  assert not py_err_set_after_what
332  assert what == expected_what
333 
334 
336  with pytest.raises(RuntimeError) as excinfo:
337  m.error_already_set_what(FlakyException, ("failure_point_init",))
338  lines = str(excinfo.value).splitlines()
339  # PyErr_NormalizeException replaces the original FlakyException with ValueError:
340  assert lines[:3] == [
341  "pybind11::error_already_set: MISMATCH of original and normalized active exception types:"
342  " ORIGINAL FlakyException REPLACED BY ValueError: triggered_failure_point_init",
343  "",
344  "At:",
345  ]
346  # Checking the first two lines of the traceback as formatted in error_string():
347  assert "test_exceptions.py(" in lines[3]
348  assert lines[3].endswith("): __init__")
349  assert lines[4].endswith(
350  "): _test_flaky_exception_failure_point_init_before_py_3_12"
351  )
352 
353 
355  # Behavior change in Python 3.12: https://github.com/python/cpython/issues/102594
356  what, py_err_set_after_what = m.error_already_set_what(
357  FlakyException, ("failure_point_init",)
358  )
359  assert not py_err_set_after_what
360  lines = what.splitlines()
361  assert lines[0].endswith("ValueError[WITH __notes__]: triggered_failure_point_init")
362  assert lines[1] == "__notes__ (len=1):"
363  assert "Normalization failed:" in lines[2]
364  assert "FlakyException" in lines[2]
365 
366 
367 @pytest.mark.skipif(
368  "env.PYPY and sys.version_info[:2] < (3, 12)",
369  reason="PyErr_NormalizeException Segmentation fault",
370 )
372  if sys.version_info[:2] < (3, 12):
374  else:
376 
377 
379  what, py_err_set_after_what = m.error_already_set_what(
380  FlakyException, ("failure_point_str",)
381  )
382  assert not py_err_set_after_what
383  lines = what.splitlines()
384  n = 3 if env.PYPY and len(lines) == 3 else 5
385  assert (
386  lines[:n]
387  == [
388  "FlakyException: <MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>",
389  "",
390  "MESSAGE UNAVAILABLE DUE TO EXCEPTION: ValueError: triggered_failure_point_str",
391  "",
392  "At:",
393  ][:n]
394  )
395 
396 
398  with pytest.raises(RuntimeError) as excinfo:
399  m.test_cross_module_interleaved_error_already_set()
400  assert str(excinfo.value) in (
401  "2nd error.", # Almost all platforms.
402  "RuntimeError: 2nd error.", # Some PyPy builds (seen under macOS).
403  )
404 
405 
407  m.test_error_already_set_double_restore(True) # dry_run
408  with pytest.raises(RuntimeError) as excinfo:
409  m.test_error_already_set_double_restore(False)
410  assert str(excinfo.value) == (
411  "Internal error: pybind11::detail::error_fetch_and_normalize::restore()"
412  " called a second time. ORIGINAL ERROR: ValueError: Random error."
413  )
414 
415 
417  # https://github.com/pybind/pybind11/issues/4075
418  what = m.test_pypy_oserror_normalization()
419  assert "this_filename_must_not_exist" in what
420 
421 
423  with pytest.raises(RuntimeError) as excinfo:
424  m.test_fn_cast_int(lambda: None)
425 
426  assert str(excinfo.value).startswith(
427  "Unable to cast Python instance of type <class 'NoneType'> to C++ type"
428  )
429 
430 
432  with pytest.raises(TypeError) as excinfo:
433  m.return_exception_void()
434  assert "Exception" in str(excinfo.value)
Eigen::internal::print
EIGEN_STRONG_INLINE Packet4f print(const Packet4f &a)
Definition: NEON/PacketMath.h:3115
test_exceptions.test_std_exception
def test_std_exception(msg)
Definition: test_exceptions.py:13
test_exceptions.test_error_already_set
def test_error_already_set(msg)
Definition: test_exceptions.py:19
test_exceptions.test_raise_from
def test_raise_from(msg)
Definition: test_exceptions.py:32
test_exceptions.FlakyException.failure_point
failure_point
Definition: test_exceptions.py:311
test_exceptions._test_flaky_exception_failure_point_init_before_py_3_12
def _test_flaky_exception_failure_point_init_before_py_3_12()
Definition: test_exceptions.py:335
hasattr
bool hasattr(handle obj, handle name)
Definition: pytypes.h:870
test_exceptions.test_error_already_set_message_with_malformed_utf8
def test_error_already_set_message_with_malformed_utf8()
Definition: test_exceptions.py:300
test_exceptions.test_throw_nested_exception
def test_throw_nested_exception()
Definition: test_exceptions.py:258
test_exceptions._test_flaky_exception_failure_point_init_py_3_12
def _test_flaky_exception_failure_point_init_py_3_12()
Definition: test_exceptions.py:354
test_exceptions.test_nested_throws
def test_nested_throws(capture)
Definition: test_exceptions.py:203
test_exceptions.test_raise_from_already_set
def test_raise_from_already_set(msg)
Definition: test_exceptions.py:39
test_exceptions.test_cross_module_exception_translator
def test_cross_module_exception_translator()
Definition: test_exceptions.py:82
isinstance
bool isinstance(handle obj)
Definition: pytypes.h:842
gtwrap.interface_parser.function.__repr__
str __repr__(self)
Definition: interface_parser/function.py:53
test_exceptions.test_flaky_exception_failure_point_init
def test_flaky_exception_failure_point_init()
Definition: test_exceptions.py:371
test_exceptions.test_python_call_in_catch
def test_python_call_in_catch()
Definition: test_exceptions.py:88
test_exceptions.test_error_already_set_message_with_unicode_surrogate
def test_error_already_set_message_with_unicode_surrogate()
Definition: test_exceptions.py:293
test_exceptions.test_exception_matches
def test_exception_matches()
Definition: test_exceptions.py:134
test_exceptions.test_fn_cast_int_exception
def test_fn_cast_int_exception()
Definition: test_exceptions.py:422
test_exceptions.test_pypy_oserror_normalization
def test_pypy_oserror_normalization()
Definition: test_exceptions.py:416
test_exceptions.test_local_translator
def test_local_translator(msg)
Definition: test_exceptions.py:275
test_exceptions.test_python_alreadyset_in_destructor
def test_python_alreadyset_in_destructor(monkeypatch, capsys)
Definition: test_exceptions.py:105
test_exceptions.FlakyException.__str__
def __str__(self)
Definition: test_exceptions.py:313
test_exceptions.ignore_pytest_unraisable_warning
def ignore_pytest_unraisable_warning(f)
Definition: test_exceptions.py:94
test_exceptions.test_cross_module_interleaved_error_already_set
def test_cross_module_interleaved_error_already_set()
Definition: test_exceptions.py:397
str
Definition: pytypes.h:1558
tree::f
Point2(* f)(const Point3 &, OptionalJacobian< 2, 3 >)
Definition: testExpression.cpp:218
test_exceptions.test_invalid_repr
def test_invalid_repr()
Definition: test_exceptions.py:266
test_exceptions.test_flaky_exception_failure_point_str
def test_flaky_exception_failure_point_str()
Definition: test_exceptions.py:378
test_exceptions.test_return_exception_void
def test_return_exception_void()
Definition: test_exceptions.py:431
test_exceptions.test_custom
def test_custom(msg)
Definition: test_exceptions.py:140
test_exceptions.FlakyException.__init__
def __init__(self, failure_point)
Definition: test_exceptions.py:308
len
size_t len(handle h)
Get the length of a Python object.
Definition: pytypes.h:2446
test_exceptions.test_error_already_set_double_restore
def test_error_already_set_double_restore()
Definition: test_exceptions.py:406
test_exceptions.test_error_already_set_what_with_happy_exceptions
def test_error_already_set_what_with_happy_exceptions(exc_type, exc_value, expected_what)
Definition: test_exceptions.py:327
test_exceptions.test_cross_module_exceptions
def test_cross_module_exceptions(msg)
Definition: test_exceptions.py:46
test_exceptions.FlakyException
Definition: test_exceptions.py:307
pybind11.msg
msg
Definition: wrap/pybind11/pybind11/__init__.py:6


gtsam
Author(s):
autogenerated on Fri Nov 1 2024 03:39:27