diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8364d7ce..fb130f8f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,24 +7,24 @@ exclude: .asv repos: - repo: https://github.com/crate-ci/typos - rev: typos-dict-v0.11.35 + rev: dictgen-v0.3.1 hooks: - id: typos - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.3 + rev: v0.9.3 hooks: - id: ruff args: [--fix, --unsafe-fixes] - id: ruff-format - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.22 + rev: v0.23 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.13.0 + rev: v1.14.1 hooks: - id: mypy exclude: tests|_throttler.pyi diff --git a/Makefile b/Makefile index 41a53d75..d8c608d1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: build check clean benchmark-all benchmark-compare typetest build: - HATCH_BUILD_HOOKS_ENABLE=1 pip install -e . + HATCH_BUILD_HOOKS_ENABLE=1 uv pip install -e . --force-reinstall check: pre-commit run --all-files diff --git a/pyproject.toml b/pyproject.toml index 1b311486..dd7fb69b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,9 @@ enable-by-default = false require-runtime-dependencies = true dependencies = [ "hatch-mypyc>=0.13.0", - "mypy>=0.991", + # FIXME: something happened in 1.14.0 that broke the build + # https://github.com/pyapp-kit/psygnal/issues/350 + "mypy==1.13.0", "mypy_extensions >=0.4.2", "pydantic!=2.10.0", "types-attrs", diff --git a/src/psygnal/__init__.py b/src/psygnal/__init__.py index 8bceb9e7..8ffd9483 100644 --- a/src/psygnal/__init__.py +++ b/src/psygnal/__init__.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any if TYPE_CHECKING: - from ._evented_model import EventedModel # noqa: TCH004 + from ._evented_model import EventedModel # noqa: TC004 try: @@ -19,20 +19,20 @@ __email__ = "talley.lambert@gmail.com" __all__ = [ - "__version__", - "_compiled", - "debounced", "EmissionInfo", - "emit_queued", "EmitLoopError", - "evented", "EventedModel", - "get_evented_namespace", - "is_evented", "Signal", "SignalGroup", "SignalGroupDescriptor", "SignalInstance", + "__version__", + "_compiled", + "debounced", + "emit_queued", + "evented", + "get_evented_namespace", + "is_evented", "throttled", ] diff --git a/src/psygnal/_evented_model.py b/src/psygnal/_evented_model.py index 427c0384..31f90f98 100644 --- a/src/psygnal/_evented_model.py +++ b/src/psygnal/_evented_model.py @@ -693,7 +693,7 @@ def enums_as_values(self, as_values: bool = True) -> Iterator[None]: @classmethod @contextmanager - def enums_as_values( + def enums_as_values( # type: ignore [misc] # Incompatible redefinition cls, as_values: bool = True ) -> Iterator[None]: # pragma: no cover """Temporarily override how enums are retrieved. diff --git a/src/psygnal/_group.py b/src/psygnal/_group.py index 59154637..a2f6ba87 100644 --- a/src/psygnal/_group.py +++ b/src/psygnal/_group.py @@ -28,7 +28,7 @@ if TYPE_CHECKING: import threading - from collections.abc import Iterable, Iterator, Mapping + from collections.abc import Container, Iterable, Iterator, Mapping from contextlib import AbstractContextManager from psygnal._signal import F, ReducerFunc @@ -155,11 +155,11 @@ def _inner(slot: Callable) -> Callable: return _inner if slot is None else _inner(slot) - def block(self, exclude: Iterable[str | SignalInstance] = ()) -> None: + def block(self, exclude: Container[str | SignalInstance] = ()) -> None: """Block this signal and all emitters from emitting.""" super().block() for name, sig in self._signals.items(): - if exclude and sig in exclude or name in exclude: + if name in exclude or sig in exclude: continue self._sig_was_blocked[name] = sig._is_blocked sig.block() @@ -172,7 +172,7 @@ def unblock(self) -> None: sig.unblock() def blocked( - self, exclude: Iterable[str | SignalInstance] = () + self, exclude: Container[str | SignalInstance] = () ) -> AbstractContextManager[None]: """Context manager to temporarily block all emitters in this group. @@ -516,14 +516,14 @@ def connect_direct( def disconnect(self, slot: Callable | None = None, missing_ok: bool = True) -> None: return self._psygnal_relay.disconnect(slot=slot, missing_ok=missing_ok) - def block(self, exclude: Iterable[str | SignalInstance] = ()) -> None: + def block(self, exclude: Container[str | SignalInstance] = ()) -> None: return self._psygnal_relay.block(exclude=exclude) def unblock(self) -> None: return self._psygnal_relay.unblock() def blocked( - self, exclude: Iterable[str | SignalInstance] = () + self, exclude: Container[str | SignalInstance] = () ) -> AbstractContextManager[None]: return self._psygnal_relay.blocked(exclude=exclude) diff --git a/src/psygnal/_group_descriptor.py b/src/psygnal/_group_descriptor.py index 73b92255..557c5436 100644 --- a/src/psygnal/_group_descriptor.py +++ b/src/psygnal/_group_descriptor.py @@ -33,7 +33,7 @@ EqOperator: TypeAlias = Callable[[Any, Any], bool] FieldAliasFunc: TypeAlias = Callable[[str], Optional[str]] -__all__ = ["is_evented", "get_evented_namespace", "SignalGroupDescriptor"] +__all__ = ["SignalGroupDescriptor", "get_evented_namespace", "is_evented"] T = TypeVar("T", bound=type) @@ -526,8 +526,7 @@ def __init__( grp_cls = signal_group_class or SignalGroup if not (isinstance(grp_cls, type) and issubclass(grp_cls, SignalGroup)): raise TypeError( # pragma: no cover - f"'signal_group_class' must be a subclass of SignalGroup, " - f"not {grp_cls}" + f"'signal_group_class' must be a subclass of SignalGroup, not {grp_cls}" ) if not collect_fields: if grp_cls is SignalGroup: diff --git a/src/psygnal/_signal.py b/src/psygnal/_signal.py index d4cea1e5..c1b0482e 100644 --- a/src/psygnal/_signal.py +++ b/src/psygnal/_signal.py @@ -165,7 +165,7 @@ def ensure_at_least_20(val: int): ) if TYPE_CHECKING: - from collections.abc import Iterable, Iterator + from collections.abc import Container, Iterable, Iterator from ._group import EmissionInfo from ._weak_callback import RefErrorChoice @@ -1057,7 +1057,7 @@ def _check_nargs( slot_sig = _get_signature_possibly_qt(slot) except ValueError as e: warnings.warn( - f"{e}. To silence this warning, connect with " "`check_nargs=False`", + f"{e}. To silence this warning, connect with `check_nargs=False`", stacklevel=4, ) return None, None, False @@ -1228,8 +1228,7 @@ def emit_fast(self, *args: Any) -> None: caller.cb(args) except RecursionError as e: raise RecursionError( - f"RecursionError when " - f"emitting signal {self.name!r} with args {args}" + f"RecursionError when emitting signal {self.name!r} with args {args}" ) from e except EmitLoopError as e: # pragma: no cover raise e @@ -1309,7 +1308,7 @@ def _run_emit_loop_queued(self) -> None: raise RecursionError i += 1 - def block(self, exclude: Iterable[str | SignalInstance] = ()) -> None: + def block(self, exclude: Container[str | SignalInstance] = ()) -> None: """Block this signal from emitting. NOTE: the `exclude` argument is only for SignalGroup subclass, but we @@ -1493,7 +1492,7 @@ class _SignalBlocker: """Context manager to block and unblock a signal.""" def __init__( - self, signal: SignalInstance, exclude: Iterable[str | SignalInstance] = () + self, signal: SignalInstance, exclude: Container[str | SignalInstance] = () ) -> None: self._signal = signal self._exclude = exclude diff --git a/src/psygnal/_weak_callback.py b/src/psygnal/_weak_callback.py index 332ccfd3..541c414a 100644 --- a/src/psygnal/_weak_callback.py +++ b/src/psygnal/_weak_callback.py @@ -24,7 +24,7 @@ RefErrorChoice: TypeAlias = Literal["raise", "warn", "ignore"] -__all__ = ["weak_callback", "WeakCallback"] +__all__ = ["WeakCallback", "weak_callback"] _T = TypeVar("_T") _R = TypeVar("_R") # return type of cb diff --git a/src/psygnal/utils.py b/src/psygnal/utils.py index 69a73147..e37a290f 100644 --- a/src/psygnal/utils.py +++ b/src/psygnal/utils.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: from collections.abc import Generator, Iterator -__all__ = ["monitor_events", "iter_signal_instances"] +__all__ = ["iter_signal_instances", "monitor_events"] def _default_event_monitor(info: EmissionInfo) -> None: diff --git a/tests/test_evented_model.py b/tests/test_evented_model.py index 12b41773..693a8b40 100644 --- a/tests/test_evented_model.py +++ b/tests/test_evented_model.py @@ -914,7 +914,7 @@ def d(self, value): ], ) def test_evented_model_reemission(mode: Union[str, dict]) -> None: - err = mode == "err" or isinstance(mode, dict) and "err" in mode.values() + err = mode == "err" or (isinstance(mode, dict) and "err" in mode.values()) with ( pytest.raises(ValueError, match="Invalid reemission") if err else nullcontext() ): diff --git a/tests/test_psygnal.py b/tests/test_psygnal.py index bae11e33..7b5b26d7 100644 --- a/tests/test_psygnal.py +++ b/tests/test_psygnal.py @@ -156,7 +156,7 @@ def boom(v: int) -> None: import re error_re = re.compile( - "signal 'tests.test_psygnal.Emitter.one_int'" f".*{re.escape(__file__)}", + f"signal 'tests.test_psygnal.Emitter.one_int'.*{re.escape(__file__)}", re.DOTALL, ) with pytest.raises(EmitLoopError, match=error_re): @@ -199,7 +199,7 @@ def bad_cb(a, b, c): ... import re error_re = re.compile( - "signal 'tests.test_psygnal.Emitter.one_int'" f".*{re.escape(__file__)}", + f"signal 'tests.test_psygnal.Emitter.one_int'.*{re.escape(__file__)}", re.DOTALL, ) with pytest.raises(EmitLoopError, match=error_re) as e: @@ -554,7 +554,7 @@ def test_connect_validation(func_name, sig_name, mode, typed): signal: SignalInstance = getattr(e, sig_name) bad_count = COUNT_INCOMPATIBLE[sig_name] bad_sig = SIG_INCOMPATIBLE[sig_name] - if func_name in bad_count or check_types and func_name in bad_sig: + if func_name in bad_count or (check_types and func_name in bad_sig): with pytest.raises(ValueError) as er: signal.connect(func, check_types=check_types) assert "Accepted signature:" in str(er)