Skip to content

Commit

Permalink
test: test for recursion error (#284)
Browse files Browse the repository at this point in the history
* test: test for recursion error

* ci: skip py39 windows
  • Loading branch information
tlambert03 authored Feb 26, 2024
1 parent d8cab76 commit 4aa62b0
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/psygnal/_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 22 additions & 1 deletion tests/test_psygnal.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import gc
import os
import sys
from contextlib import suppress
from functools import partial, wraps
from inspect import Signature
from typing import Literal, Optional
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):
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -967,3 +976,15 @@ def test_pickle():
x = pickle.loads(_dump)
x.sig.emit()
mock.assert_called_once()


@pytest.mark.skipif(PY39 and WINDOWS and COMPILED, reason="fails")
def test_recursion_error() -> None:
s = SignalInstance()

@s.connect
def callback() -> None:
s.emit()

with pytest.raises(RecursionError):
s.emit()
3 changes: 2 additions & 1 deletion tests/test_weak_callable.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from weakref import ref

import pytest
import toolz

from psygnal._weak_callback import WeakCallback, weak_callback

Expand Down Expand Up @@ -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:
Expand All @@ -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)
Expand Down

0 comments on commit 4aa62b0

Please sign in to comment.