Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:53

0001 import sys
0002 
0003 import pytest
0004 
0005 import env
0006 import pybind11_cross_module_tests as cm
0007 import pybind11_tests  # noqa: F401
0008 from pybind11_tests import exceptions as m
0009 
0010 
0011 def test_std_exception(msg):
0012     with pytest.raises(RuntimeError) as excinfo:
0013         m.throw_std_exception()
0014     assert msg(excinfo.value) == "This exception was intentionally thrown."
0015 
0016 
0017 def test_error_already_set(msg):
0018     with pytest.raises(RuntimeError) as excinfo:
0019         m.throw_already_set(False)
0020     assert (
0021         msg(excinfo.value)
0022         == "Internal error: pybind11::error_already_set called while Python error indicator not set."
0023     )
0024 
0025     with pytest.raises(ValueError) as excinfo:
0026         m.throw_already_set(True)
0027     assert msg(excinfo.value) == "foo"
0028 
0029 
0030 def test_raise_from(msg):
0031     with pytest.raises(ValueError) as excinfo:
0032         m.raise_from()
0033     assert msg(excinfo.value) == "outer"
0034     assert msg(excinfo.value.__cause__) == "inner"
0035 
0036 
0037 def test_raise_from_already_set(msg):
0038     with pytest.raises(ValueError) as excinfo:
0039         m.raise_from_already_set()
0040     assert msg(excinfo.value) == "outer"
0041     assert msg(excinfo.value.__cause__) == "inner"
0042 
0043 
0044 def test_cross_module_exceptions(msg):
0045     with pytest.raises(RuntimeError) as excinfo:
0046         cm.raise_runtime_error()
0047     assert str(excinfo.value) == "My runtime error"
0048 
0049     with pytest.raises(ValueError) as excinfo:
0050         cm.raise_value_error()
0051     assert str(excinfo.value) == "My value error"
0052 
0053     with pytest.raises(ValueError) as excinfo:
0054         cm.throw_pybind_value_error()
0055     assert str(excinfo.value) == "pybind11 value error"
0056 
0057     with pytest.raises(TypeError) as excinfo:
0058         cm.throw_pybind_type_error()
0059     assert str(excinfo.value) == "pybind11 type error"
0060 
0061     with pytest.raises(StopIteration) as excinfo:
0062         cm.throw_stop_iteration()
0063 
0064     with pytest.raises(cm.LocalSimpleException) as excinfo:
0065         cm.throw_local_simple_error()
0066     assert msg(excinfo.value) == "external mod"
0067 
0068     with pytest.raises(KeyError) as excinfo:
0069         cm.throw_local_error()
0070     # KeyError is a repr of the key, so it has an extra set of quotes
0071     assert str(excinfo.value) == "'just local'"
0072 
0073 
0074 # TODO: FIXME
0075 @pytest.mark.xfail(
0076     "env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang'))",
0077     raises=RuntimeError,
0078     reason="See Issue #2847, PR #2999, PR #4324",
0079 )
0080 def test_cross_module_exception_translator():
0081     with pytest.raises(KeyError):
0082         # translator registered in cross_module_tests
0083         m.throw_should_be_translated_to_key_error()
0084 
0085 
0086 def test_python_call_in_catch():
0087     d = {}
0088     assert m.python_call_in_destructor(d) is True
0089     assert d["good"] is True
0090 
0091 
0092 def ignore_pytest_unraisable_warning(f):
0093     unraisable = "PytestUnraisableExceptionWarning"
0094     if hasattr(pytest, unraisable):  # Python >= 3.8 and pytest >= 6
0095         dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
0096         return dec(f)
0097     else:
0098         return f
0099 
0100 
0101 # TODO: find out why this fails on PyPy, https://foss.heptapod.net/pypy/pypy/-/issues/3583
0102 @pytest.mark.xfail(env.PYPY, reason="Failure on PyPy 3.8 (7.3.7)", strict=False)
0103 @ignore_pytest_unraisable_warning
0104 def test_python_alreadyset_in_destructor(monkeypatch, capsys):
0105     hooked = False
0106     triggered = False
0107 
0108     if hasattr(sys, "unraisablehook"):  # Python 3.8+
0109         hooked = True
0110         # Don't take `sys.unraisablehook`, as that's overwritten by pytest
0111         default_hook = sys.__unraisablehook__
0112 
0113         def hook(unraisable_hook_args):
0114             exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args
0115             if obj == "already_set demo":
0116                 nonlocal triggered
0117                 triggered = True
0118             default_hook(unraisable_hook_args)
0119             return
0120 
0121         # Use monkeypatch so pytest can apply and remove the patch as appropriate
0122         monkeypatch.setattr(sys, "unraisablehook", hook)
0123 
0124     assert m.python_alreadyset_in_destructor("already_set demo") is True
0125     if hooked:
0126         assert triggered is True
0127 
0128     _, captured_stderr = capsys.readouterr()
0129     assert captured_stderr.startswith("Exception ignored in: 'already_set demo'")
0130     assert captured_stderr.rstrip().endswith("KeyError: 'bar'")
0131 
0132 
0133 def test_exception_matches():
0134     assert m.exception_matches()
0135     assert m.exception_matches_base()
0136     assert m.modulenotfound_exception_matches_base()
0137 
0138 
0139 def test_custom(msg):
0140     # Can we catch a MyException?
0141     with pytest.raises(m.MyException) as excinfo:
0142         m.throws1()
0143     assert msg(excinfo.value) == "this error should go to a custom type"
0144 
0145     # Can we translate to standard Python exceptions?
0146     with pytest.raises(RuntimeError) as excinfo:
0147         m.throws2()
0148     assert msg(excinfo.value) == "this error should go to a standard Python exception"
0149 
0150     # Can we handle unknown exceptions?
0151     with pytest.raises(RuntimeError) as excinfo:
0152         m.throws3()
0153     assert msg(excinfo.value) == "Caught an unknown exception!"
0154 
0155     # Can we delegate to another handler by rethrowing?
0156     with pytest.raises(m.MyException) as excinfo:
0157         m.throws4()
0158     assert msg(excinfo.value) == "this error is rethrown"
0159 
0160     # Can we fall-through to the default handler?
0161     with pytest.raises(RuntimeError) as excinfo:
0162         m.throws_logic_error()
0163     assert (
0164         msg(excinfo.value) == "this error should fall through to the standard handler"
0165     )
0166 
0167     # OverFlow error translation.
0168     with pytest.raises(OverflowError) as excinfo:
0169         m.throws_overflow_error()
0170 
0171     # Can we handle a helper-declared exception?
0172     with pytest.raises(m.MyException5) as excinfo:
0173         m.throws5()
0174     assert msg(excinfo.value) == "this is a helper-defined translated exception"
0175 
0176     # Exception subclassing:
0177     with pytest.raises(m.MyException5) as excinfo:
0178         m.throws5_1()
0179     assert msg(excinfo.value) == "MyException5 subclass"
0180     assert isinstance(excinfo.value, m.MyException5_1)
0181 
0182     with pytest.raises(m.MyException5_1) as excinfo:
0183         m.throws5_1()
0184     assert msg(excinfo.value) == "MyException5 subclass"
0185 
0186     with pytest.raises(m.MyException5) as excinfo:
0187         try:
0188             m.throws5()
0189         except m.MyException5_1 as err:
0190             raise RuntimeError("Exception error: caught child from parent") from err
0191     assert msg(excinfo.value) == "this is a helper-defined translated exception"
0192 
0193 
0194 def test_nested_throws(capture):
0195     """Tests nested (e.g. C++ -> Python -> C++) exception handling"""
0196 
0197     def throw_myex():
0198         raise m.MyException("nested error")
0199 
0200     def throw_myex5():
0201         raise m.MyException5("nested error 5")
0202 
0203     # In the comments below, the exception is caught in the first step, thrown in the last step
0204 
0205     # C++ -> Python
0206     with capture:
0207         m.try_catch(m.MyException5, throw_myex5)
0208     assert str(capture).startswith("MyException5: nested error 5")
0209 
0210     # Python -> C++ -> Python
0211     with pytest.raises(m.MyException) as excinfo:
0212         m.try_catch(m.MyException5, throw_myex)
0213     assert str(excinfo.value) == "nested error"
0214 
0215     def pycatch(exctype, f, *args):
0216         try:
0217             f(*args)
0218         except m.MyException as e:
0219             print(e)
0220 
0221     # C++ -> Python -> C++ -> Python
0222     with capture:
0223         m.try_catch(
0224             m.MyException5,
0225             pycatch,
0226             m.MyException,
0227             m.try_catch,
0228             m.MyException,
0229             throw_myex5,
0230         )
0231     assert str(capture).startswith("MyException5: nested error 5")
0232 
0233     # C++ -> Python -> C++
0234     with capture:
0235         m.try_catch(m.MyException, pycatch, m.MyException5, m.throws4)
0236     assert capture == "this error is rethrown"
0237 
0238     # Python -> C++ -> Python -> C++
0239     with pytest.raises(m.MyException5) as excinfo:
0240         m.try_catch(m.MyException, pycatch, m.MyException, m.throws5)
0241     assert str(excinfo.value) == "this is a helper-defined translated exception"
0242 
0243 
0244 def test_throw_nested_exception():
0245     with pytest.raises(RuntimeError) as excinfo:
0246         m.throw_nested_exception()
0247     assert str(excinfo.value) == "Outer Exception"
0248     assert str(excinfo.value.__cause__) == "Inner Exception"
0249 
0250 
0251 # This can often happen if you wrap a pybind11 class in a Python wrapper
0252 def test_invalid_repr():
0253     class MyRepr:
0254         def __repr__(self):
0255             raise AttributeError("Example error")
0256 
0257     with pytest.raises(TypeError):
0258         m.simple_bool_passthrough(MyRepr())
0259 
0260 
0261 def test_local_translator(msg):
0262     """Tests that a local translator works and that the local translator from
0263     the cross module is not applied"""
0264     with pytest.raises(RuntimeError) as excinfo:
0265         m.throws6()
0266     assert msg(excinfo.value) == "MyException6 only handled in this module"
0267 
0268     with pytest.raises(RuntimeError) as excinfo:
0269         m.throws_local_error()
0270     assert not isinstance(excinfo.value, KeyError)
0271     assert msg(excinfo.value) == "never caught"
0272 
0273     with pytest.raises(Exception) as excinfo:
0274         m.throws_local_simple_error()
0275     assert not isinstance(excinfo.value, cm.LocalSimpleException)
0276     assert msg(excinfo.value) == "this mod"
0277 
0278 
0279 def test_error_already_set_message_with_unicode_surrogate():  # Issue #4288
0280     assert m.error_already_set_what(RuntimeError, "\ud927") == (
0281         "RuntimeError: \\ud927",
0282         False,
0283     )
0284 
0285 
0286 def test_error_already_set_message_with_malformed_utf8():
0287     assert m.error_already_set_what(RuntimeError, b"\x80") == (
0288         "RuntimeError: b'\\x80'",
0289         False,
0290     )
0291 
0292 
0293 class FlakyException(Exception):
0294     def __init__(self, failure_point):
0295         if failure_point == "failure_point_init":
0296             raise ValueError("triggered_failure_point_init")
0297         self.failure_point = failure_point
0298 
0299     def __str__(self):
0300         if self.failure_point == "failure_point_str":
0301             raise ValueError("triggered_failure_point_str")
0302         return "FlakyException.__str__"
0303 
0304 
0305 @pytest.mark.parametrize(
0306     "exc_type, exc_value, expected_what",
0307     (
0308         (ValueError, "plain_str", "ValueError: plain_str"),
0309         (ValueError, ("tuple_elem",), "ValueError: tuple_elem"),
0310         (FlakyException, ("happy",), "FlakyException: FlakyException.__str__"),
0311     ),
0312 )
0313 def test_error_already_set_what_with_happy_exceptions(
0314     exc_type, exc_value, expected_what
0315 ):
0316     what, py_err_set_after_what = m.error_already_set_what(exc_type, exc_value)
0317     assert not py_err_set_after_what
0318     assert what == expected_what
0319 
0320 
0321 @pytest.mark.skipif("env.PYPY", reason="PyErr_NormalizeException Segmentation fault")
0322 def test_flaky_exception_failure_point_init():
0323     with pytest.raises(RuntimeError) as excinfo:
0324         m.error_already_set_what(FlakyException, ("failure_point_init",))
0325     lines = str(excinfo.value).splitlines()
0326     # PyErr_NormalizeException replaces the original FlakyException with ValueError:
0327     assert lines[:3] == [
0328         "pybind11::error_already_set: MISMATCH of original and normalized active exception types:"
0329         " ORIGINAL FlakyException REPLACED BY ValueError: triggered_failure_point_init",
0330         "",
0331         "At:",
0332     ]
0333     # Checking the first two lines of the traceback as formatted in error_string():
0334     assert "test_exceptions.py(" in lines[3]
0335     assert lines[3].endswith("): __init__")
0336     assert lines[4].endswith("): test_flaky_exception_failure_point_init")
0337 
0338 
0339 def test_flaky_exception_failure_point_str():
0340     what, py_err_set_after_what = m.error_already_set_what(
0341         FlakyException, ("failure_point_str",)
0342     )
0343     assert not py_err_set_after_what
0344     lines = what.splitlines()
0345     if env.PYPY and len(lines) == 3:
0346         n = 3  # Traceback is missing.
0347     else:
0348         n = 5
0349     assert (
0350         lines[:n]
0351         == [
0352             "FlakyException: <MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>",
0353             "",
0354             "MESSAGE UNAVAILABLE DUE TO EXCEPTION: ValueError: triggered_failure_point_str",
0355             "",
0356             "At:",
0357         ][:n]
0358     )
0359 
0360 
0361 def test_cross_module_interleaved_error_already_set():
0362     with pytest.raises(RuntimeError) as excinfo:
0363         m.test_cross_module_interleaved_error_already_set()
0364     assert str(excinfo.value) in (
0365         "2nd error.",  # Almost all platforms.
0366         "RuntimeError: 2nd error.",  # Some PyPy builds (seen under macOS).
0367     )
0368 
0369 
0370 def test_error_already_set_double_restore():
0371     m.test_error_already_set_double_restore(True)  # dry_run
0372     with pytest.raises(RuntimeError) as excinfo:
0373         m.test_error_already_set_double_restore(False)
0374     assert str(excinfo.value) == (
0375         "Internal error: pybind11::detail::error_fetch_and_normalize::restore()"
0376         " called a second time. ORIGINAL ERROR: ValueError: Random error."
0377     )
0378 
0379 
0380 def test_pypy_oserror_normalization():
0381     # https://github.com/pybind/pybind11/issues/4075
0382     what = m.test_pypy_oserror_normalization()
0383     assert "this_filename_must_not_exist" in what