diff options
author | myrtle <gatecat@ds0.me> | 2022-09-15 09:06:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-15 09:06:35 +0200 |
commit | 3983d4fe53e2c609a5c76510aff8e998a4c22285 (patch) | |
tree | 1c4a543f661dd1b281aecf4660388491702fa8d8 /3rdparty/pybind11/tests/test_exceptions.py | |
parent | f1349e114f3a16ccd002e8513339e18f5be4d31b (diff) | |
parent | a72f898ff4c4237424c468044a6db9d6953b541e (diff) | |
download | nextpnr-3983d4fe53e2c609a5c76510aff8e998a4c22285.tar.gz nextpnr-3983d4fe53e2c609a5c76510aff8e998a4c22285.tar.bz2 nextpnr-3983d4fe53e2c609a5c76510aff8e998a4c22285.zip |
Merge pull request #1024 from YosysHQ/gatecat/pybind11-bump
3rdparty: Bump vendored pybind11 version for py3.11 support
Diffstat (limited to '3rdparty/pybind11/tests/test_exceptions.py')
-rw-r--r-- | 3rdparty/pybind11/tests/test_exceptions.py | 196 |
1 files changed, 183 insertions, 13 deletions
diff --git a/3rdparty/pybind11/tests/test_exceptions.py b/3rdparty/pybind11/tests/test_exceptions.py index 95eac709..70b6ffea 100644 --- a/3rdparty/pybind11/tests/test_exceptions.py +++ b/3rdparty/pybind11/tests/test_exceptions.py @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- import sys import pytest -from pybind11_tests import exceptions as m +import env import pybind11_cross_module_tests as cm +from pybind11_tests import exceptions as m def test_std_exception(msg): @@ -16,14 +16,31 @@ def test_std_exception(msg): def test_error_already_set(msg): with pytest.raises(RuntimeError) as excinfo: m.throw_already_set(False) - assert msg(excinfo.value) == "Unknown internal error occurred" + assert ( + msg(excinfo.value) + == "Internal error: pybind11::error_already_set called while Python error indicator not set." + ) with pytest.raises(ValueError) as excinfo: m.throw_already_set(True) assert msg(excinfo.value) == "foo" -def test_cross_module_exceptions(): +def test_raise_from(msg): + with pytest.raises(ValueError) as excinfo: + m.raise_from() + assert msg(excinfo.value) == "outer" + assert msg(excinfo.value.__cause__) == "inner" + + +def test_raise_from_already_set(msg): + with pytest.raises(ValueError) as excinfo: + m.raise_from_already_set() + assert msg(excinfo.value) == "outer" + assert msg(excinfo.value.__cause__) == "inner" + + +def test_cross_module_exceptions(msg): with pytest.raises(RuntimeError) as excinfo: cm.raise_runtime_error() assert str(excinfo.value) == "My runtime error" @@ -43,6 +60,27 @@ def test_cross_module_exceptions(): with pytest.raises(StopIteration) as excinfo: cm.throw_stop_iteration() + with pytest.raises(cm.LocalSimpleException) as excinfo: + cm.throw_local_simple_error() + assert msg(excinfo.value) == "external mod" + + with pytest.raises(KeyError) as excinfo: + cm.throw_local_error() + # KeyError is a repr of the key, so it has an extra set of quotes + assert str(excinfo.value) == "'just local'" + + +# TODO: FIXME +@pytest.mark.xfail( + "env.PYPY and env.MACOS", + raises=RuntimeError, + reason="Expected failure with PyPy and libc++ (Issue #2847 & PR #2999)", +) +def test_cross_module_exception_translator(): + with pytest.raises(KeyError): + # translator registered in cross_module_tests + m.throw_should_be_translated_to_key_error() + def test_python_call_in_catch(): d = {} @@ -50,18 +88,32 @@ def test_python_call_in_catch(): assert d["good"] is True +def ignore_pytest_unraisable_warning(f): + unraisable = "PytestUnraisableExceptionWarning" + if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6 + dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}") + return dec(f) + else: + return f + + +# TODO: find out why this fails on PyPy, https://foss.heptapod.net/pypy/pypy/-/issues/3583 +@pytest.mark.xfail(env.PYPY, reason="Failure on PyPy 3.8 (7.3.7)", strict=False) +@ignore_pytest_unraisable_warning def test_python_alreadyset_in_destructor(monkeypatch, capsys): hooked = False - triggered = [False] # mutable, so Python 2.7 closure can modify it + triggered = False if hasattr(sys, "unraisablehook"): # Python 3.8+ hooked = True - default_hook = sys.unraisablehook + # Don't take `sys.unraisablehook`, as that's overwritten by pytest + default_hook = sys.__unraisablehook__ def hook(unraisable_hook_args): exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args if obj == "already_set demo": - triggered[0] = True + nonlocal triggered + triggered = True default_hook(unraisable_hook_args) return @@ -70,11 +122,11 @@ def test_python_alreadyset_in_destructor(monkeypatch, capsys): assert m.python_alreadyset_in_destructor("already_set demo") is True if hooked: - assert triggered[0] is True + assert triggered is True _, captured_stderr = capsys.readouterr() - # Error message is different in Python 2 and 3, check for words that appear in both - assert "ignored" in captured_stderr and "already_set demo" in captured_stderr + assert captured_stderr.startswith("Exception ignored in: 'already_set demo'") + assert captured_stderr.rstrip().endswith("KeyError: 'bar'") def test_exception_matches(): @@ -133,8 +185,8 @@ def test_custom(msg): with pytest.raises(m.MyException5) as excinfo: try: m.throws5() - except m.MyException5_1: - raise RuntimeError("Exception error: caught child from parent") + except m.MyException5_1 as err: + raise RuntimeError("Exception error: caught child from parent") from err assert msg(excinfo.value) == "this is a helper-defined translated exception" @@ -188,11 +240,129 @@ def test_nested_throws(capture): assert str(excinfo.value) == "this is a helper-defined translated exception" +def test_throw_nested_exception(): + with pytest.raises(RuntimeError) as excinfo: + m.throw_nested_exception() + assert str(excinfo.value) == "Outer Exception" + assert str(excinfo.value.__cause__) == "Inner Exception" + + # This can often happen if you wrap a pybind11 class in a Python wrapper def test_invalid_repr(): - class MyRepr(object): + class MyRepr: def __repr__(self): raise AttributeError("Example error") with pytest.raises(TypeError): m.simple_bool_passthrough(MyRepr()) + + +def test_local_translator(msg): + """Tests that a local translator works and that the local translator from + the cross module is not applied""" + with pytest.raises(RuntimeError) as excinfo: + m.throws6() + assert msg(excinfo.value) == "MyException6 only handled in this module" + + with pytest.raises(RuntimeError) as excinfo: + m.throws_local_error() + assert not isinstance(excinfo.value, KeyError) + assert msg(excinfo.value) == "never caught" + + with pytest.raises(Exception) as excinfo: + m.throws_local_simple_error() + assert not isinstance(excinfo.value, cm.LocalSimpleException) + assert msg(excinfo.value) == "this mod" + + +class FlakyException(Exception): + def __init__(self, failure_point): + if failure_point == "failure_point_init": + raise ValueError("triggered_failure_point_init") + self.failure_point = failure_point + + def __str__(self): + if self.failure_point == "failure_point_str": + raise ValueError("triggered_failure_point_str") + return "FlakyException.__str__" + + +@pytest.mark.parametrize( + "exc_type, exc_value, expected_what", + ( + (ValueError, "plain_str", "ValueError: plain_str"), + (ValueError, ("tuple_elem",), "ValueError: tuple_elem"), + (FlakyException, ("happy",), "FlakyException: FlakyException.__str__"), + ), +) +def test_error_already_set_what_with_happy_exceptions( + exc_type, exc_value, expected_what +): + what, py_err_set_after_what = m.error_already_set_what(exc_type, exc_value) + assert not py_err_set_after_what + assert what == expected_what + + +@pytest.mark.skipif("env.PYPY", reason="PyErr_NormalizeException Segmentation fault") +def test_flaky_exception_failure_point_init(): + with pytest.raises(RuntimeError) as excinfo: + m.error_already_set_what(FlakyException, ("failure_point_init",)) + lines = str(excinfo.value).splitlines() + # PyErr_NormalizeException replaces the original FlakyException with ValueError: + assert lines[:3] == [ + "pybind11::error_already_set: MISMATCH of original and normalized active exception types:" + " ORIGINAL FlakyException REPLACED BY ValueError: triggered_failure_point_init", + "", + "At:", + ] + # Checking the first two lines of the traceback as formatted in error_string(): + assert "test_exceptions.py(" in lines[3] + assert lines[3].endswith("): __init__") + assert lines[4].endswith("): test_flaky_exception_failure_point_init") + + +def test_flaky_exception_failure_point_str(): + what, py_err_set_after_what = m.error_already_set_what( + FlakyException, ("failure_point_str",) + ) + assert not py_err_set_after_what + lines = what.splitlines() + if env.PYPY and len(lines) == 3: + n = 3 # Traceback is missing. + else: + n = 5 + assert ( + lines[:n] + == [ + "FlakyException: <MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>", + "", + "MESSAGE UNAVAILABLE DUE TO EXCEPTION: ValueError: triggered_failure_point_str", + "", + "At:", + ][:n] + ) + + +def test_cross_module_interleaved_error_already_set(): + with pytest.raises(RuntimeError) as excinfo: + m.test_cross_module_interleaved_error_already_set() + assert str(excinfo.value) in ( + "2nd error.", # Almost all platforms. + "RuntimeError: 2nd error.", # Some PyPy builds (seen under macOS). + ) + + +def test_error_already_set_double_restore(): + m.test_error_already_set_double_restore(True) # dry_run + with pytest.raises(RuntimeError) as excinfo: + m.test_error_already_set_double_restore(False) + assert str(excinfo.value) == ( + "Internal error: pybind11::detail::error_fetch_and_normalize::restore()" + " called a second time. ORIGINAL ERROR: ValueError: Random error." + ) + + +def test_pypy_oserror_normalization(): + # https://github.com/pybind/pybind11/issues/4075 + what = m.test_pypy_oserror_normalization() + assert "this_filename_must_not_exist" in what |