From a76962a584a9b7e532fb7aa6e302fdcbe3a4daab Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 24 Feb 2024 15:14:06 -0500 Subject: [PATCH 1/2] test: test for recursion error --- src/psygnal/_signal.py | 5 +++++ tests/test_psygnal.py | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/psygnal/_signal.py b/src/psygnal/_signal.py index f6f3b5b8..503d2e50 100644 --- a/src/psygnal/_signal.py +++ b/src/psygnal/_signal.py @@ -951,6 +951,11 @@ def _run_emit_loop(self, args: tuple[Any, ...]) -> None: for caller in self._slots: try: caller.cb(args) + except RecursionError as e: + raise RecursionError( + f"RecursionError in {caller.slot_repr()} when " + f"emitting signal {self.name!r} with args {args}" + ) from e except Exception as e: raise EmitLoopError( cb=caller, args=args, exc=e, signal=self diff --git a/tests/test_psygnal.py b/tests/test_psygnal.py index d47ac22b..82d1407e 100644 --- a/tests/test_psygnal.py +++ b/tests/test_psygnal.py @@ -967,3 +967,14 @@ def test_pickle(): x = pickle.loads(_dump) x.sig.emit() mock.assert_called_once() + + +def test_recursion_error() -> None: + s = SignalInstance() + + @s.connect + def callback() -> None: + s.emit() + + with pytest.raises(RecursionError): + s.emit() From 158cfb888bcd8a2bb945b082ae6c22c54688327c Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 25 Feb 2024 09:48:28 -0500 Subject: [PATCH 2/2] ci: skip py39 windows --- tests/test_psygnal.py | 12 +++++++++++- tests/test_weak_callable.py | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/test_psygnal.py b/tests/test_psygnal.py index 82d1407e..8a3633c5 100644 --- a/tests/test_psygnal.py +++ b/tests/test_psygnal.py @@ -1,4 +1,6 @@ import gc +import os +import sys from contextlib import suppress from functools import partial, wraps from inspect import Signature @@ -6,11 +8,15 @@ from unittest.mock import MagicMock, Mock, call import pytest -import toolz +import psygnal from psygnal import EmitLoopError, Signal, SignalInstance from psygnal._weak_callback import WeakCallback +PY39 = sys.version_info[:2] == (3, 9) +WINDOWS = os.name == "nt" +COMPILED = psygnal._compiled + def stupid_decorator(fun): def _fun(*args): @@ -268,8 +274,10 @@ def test_slot_types(type_: str) -> None: elif type_ == "partial_method": signal.connect(partial(obj.f_int_int, 2)) elif type_ == "toolz_function": + toolz = pytest.importorskip("toolz") signal.connect(toolz.curry(f_int_int, 2)) elif type_ == "toolz_method": + toolz = pytest.importorskip("toolz") signal.connect(toolz.curry(obj.f_int_int, 2)) elif type_ == "partial_method_kwarg": signal.connect(partial(obj.f_int_int, b=2)) @@ -362,6 +370,7 @@ def test_weakref(slot): if slot == "partial": emitter.one_int.connect(partial(obj.f_int_int, 1)) elif slot == "toolz_curry": + toolz = pytest.importorskip("toolz") emitter.one_int.connect(toolz.curry(obj.f_int_int, 1)) else: emitter.one_int.connect(getattr(obj, slot)) @@ -969,6 +978,7 @@ def test_pickle(): mock.assert_called_once() +@pytest.mark.skipif(PY39 and WINDOWS and COMPILED, reason="fails") def test_recursion_error() -> None: s = SignalInstance() diff --git a/tests/test_weak_callable.py b/tests/test_weak_callable.py index 70eccf80..4dc06b98 100644 --- a/tests/test_weak_callable.py +++ b/tests/test_weak_callable.py @@ -5,7 +5,6 @@ from weakref import ref import pytest -import toolz from psygnal._weak_callback import WeakCallback, weak_callback @@ -59,6 +58,7 @@ def obj(x: int) -> None: cb = weak_callback(obj, strong_func=(type_ == "function"), finalize=final_mock) elif type_ == "toolz_function": + toolz = pytest.importorskip("toolz") @toolz.curry def obj(z: int, x: int) -> None: @@ -73,6 +73,7 @@ def obj(z: int, x: int) -> None: elif type_ == "partial_method": cb = weak_callback(partial(obj.method, 2), max_args=0, finalize=final_mock) elif type_ == "toolz_method": + toolz = pytest.importorskip("toolz") cb = weak_callback(toolz.curry(obj.method, 2), max_args=0, finalize=final_mock) elif type_ == "mock": cb = weak_callback(mock, finalize=final_mock)