diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8fb3cfb5..81da5b9f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ exclude: .asv repos: - repo: https://github.com/compilerla/conventional-pre-commit - rev: v3.0.0 + rev: v3.1.0 hooks: - id: conventional-pre-commit stages: [commit-msg] @@ -22,18 +22,18 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.9 + rev: v0.2.0 hooks: - id: ruff args: [--fix, --unsafe-fixes] - repo: https://github.com/psf/black - rev: 23.12.1 + rev: 24.1.1 hooks: - id: black - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.15 + rev: v0.16 hooks: - id: validate-pyproject diff --git a/benchmarks/_evented.py b/benchmarks/_evented.py index 5c11fb36..3bcb9e93 100644 --- a/benchmarks/_evented.py +++ b/benchmarks/_evented.py @@ -1,5 +1,6 @@ """This script isn't run by asv... but can be run directly to see how evented dataclasses compare to non-evented dataclasses.""" + import timeit from dataclasses import dataclass from typing import ClassVar diff --git a/src/psygnal/_dataclass_utils.py b/src/psygnal/_dataclass_utils.py index ed8a9ab3..f5b72765 100644 --- a/src/psygnal/_dataclass_utils.py +++ b/src/psygnal/_dataclass_utils.py @@ -45,13 +45,11 @@ class DataClassType: @overload -def is_dataclass(obj: type) -> TypeGuard[type[DataClassType]]: - ... +def is_dataclass(obj: type) -> TypeGuard[type[DataClassType]]: ... @overload -def is_dataclass(obj: object) -> TypeGuard[DataClassType]: - ... +def is_dataclass(obj: object) -> TypeGuard[DataClassType]: ... def is_dataclass(obj: object) -> TypeGuard[DataClassType]: @@ -65,13 +63,11 @@ def is_dataclass(obj: object) -> TypeGuard[DataClassType]: @overload -def is_attrs_class(obj: type) -> TypeGuard[type[AttrsType]]: - ... +def is_attrs_class(obj: type) -> TypeGuard[type[AttrsType]]: ... @overload -def is_attrs_class(obj: object) -> TypeGuard[AttrsType]: - ... +def is_attrs_class(obj: object) -> TypeGuard[AttrsType]: ... def is_attrs_class(obj: object) -> TypeGuard[type[AttrsType]]: @@ -82,13 +78,11 @@ def is_attrs_class(obj: object) -> TypeGuard[type[AttrsType]]: @overload -def is_pydantic_model(obj: type) -> TypeGuard[type[BaseModel]]: - ... +def is_pydantic_model(obj: type) -> TypeGuard[type[BaseModel]]: ... @overload -def is_pydantic_model(obj: object) -> TypeGuard[BaseModel]: - ... +def is_pydantic_model(obj: object) -> TypeGuard[BaseModel]: ... def is_pydantic_model(obj: object) -> TypeGuard[BaseModel]: @@ -99,13 +93,11 @@ def is_pydantic_model(obj: object) -> TypeGuard[BaseModel]: @overload -def is_msgspec_struct(obj: type) -> TypeGuard[type[msgspec.Struct]]: - ... +def is_msgspec_struct(obj: type) -> TypeGuard[type[msgspec.Struct]]: ... @overload -def is_msgspec_struct(obj: object) -> TypeGuard[msgspec.Struct]: - ... +def is_msgspec_struct(obj: object) -> TypeGuard[msgspec.Struct]: ... def is_msgspec_struct(obj: object) -> TypeGuard[msgspec.Struct]: diff --git a/src/psygnal/_evented_decorator.py b/src/psygnal/_evented_decorator.py index 4b1a8a65..eb50950c 100644 --- a/src/psygnal/_evented_decorator.py +++ b/src/psygnal/_evented_decorator.py @@ -32,8 +32,7 @@ def evented( equality_operators: Optional[Dict[str, EqOperator]] = None, warn_on_no_fields: bool = ..., cache_on_instance: bool = ..., -) -> T: - ... +) -> T: ... @overload @@ -44,8 +43,7 @@ def evented( equality_operators: Optional[Dict[str, EqOperator]] = None, warn_on_no_fields: bool = ..., cache_on_instance: bool = ..., -) -> Callable[[T], T]: - ... +) -> Callable[[T], T]: ... def evented( diff --git a/src/psygnal/_group.py b/src/psygnal/_group.py index bed9b8f0..68cf3014 100644 --- a/src/psygnal/_group.py +++ b/src/psygnal/_group.py @@ -7,6 +7,7 @@ the args that were emitted. """ + from __future__ import annotations from typing import ( diff --git a/src/psygnal/_group_descriptor.py b/src/psygnal/_group_descriptor.py index 682ea057..47e8fe35 100644 --- a/src/psygnal/_group_descriptor.py +++ b/src/psygnal/_group_descriptor.py @@ -211,15 +211,13 @@ def __exit__(self, *args: Any) -> None: @overload -def evented_setattr(signal_group_name: str, super_setattr: SetAttr) -> SetAttr: - ... +def evented_setattr(signal_group_name: str, super_setattr: SetAttr) -> SetAttr: ... @overload def evented_setattr( signal_group_name: str, super_setattr: Literal[None] | None = None -) -> Callable[[SetAttr], SetAttr]: - ... +) -> Callable[[SetAttr], SetAttr]: ... def evented_setattr( @@ -412,12 +410,10 @@ def _do_patch_setattr(self, owner: type) -> None: _instance_map: ClassVar[dict[int, SignalGroup]] = {} @overload - def __get__(self, instance: None, owner: type) -> SignalGroupDescriptor: - ... + def __get__(self, instance: None, owner: type) -> SignalGroupDescriptor: ... @overload - def __get__(self, instance: object, owner: type) -> SignalGroup: - ... + def __get__(self, instance: object, owner: type) -> SignalGroup: ... def __get__( self, instance: object, owner: type diff --git a/src/psygnal/_queue.py b/src/psygnal/_queue.py index 56e51af0..4bda10f4 100644 --- a/src/psygnal/_queue.py +++ b/src/psygnal/_queue.py @@ -31,9 +31,9 @@ class QueuedCallback(WeakCallback): thread will be used. """ - _GLOBAL_QUEUE: ClassVar[ - collections.defaultdict[Thread, Queue[CbArgsTuple]] - ] = DefaultDict(Queue) + _GLOBAL_QUEUE: ClassVar[collections.defaultdict[Thread, Queue[CbArgsTuple]]] = ( + DefaultDict(Queue) + ) def __init__( self, diff --git a/src/psygnal/_signal.py b/src/psygnal/_signal.py index d5460455..8e8af43b 100644 --- a/src/psygnal/_signal.py +++ b/src/psygnal/_signal.py @@ -140,12 +140,14 @@ def __set_name__(self, owner: type[Any], name: str) -> None: self._name = name @overload - def __get__(self, instance: None, owner: type[Any] | None = None) -> Signal: - ... # pragma: no cover + def __get__( + self, instance: None, owner: type[Any] | None = None + ) -> Signal: ... # pragma: no cover @overload - def __get__(self, instance: Any, owner: type[Any] | None = None) -> SignalInstance: - ... # pragma: no cover + def __get__( + self, instance: Any, owner: type[Any] | None = None + ) -> SignalInstance: ... # pragma: no cover def __get__( self, instance: Any, owner: type[Any] | None = None @@ -358,8 +360,7 @@ def connect( unique: bool | str = ..., max_args: int | None = None, on_ref_error: RefErrorChoice = ..., - ) -> Callable[[F], F]: - ... # pragma: no cover + ) -> Callable[[F], F]: ... # pragma: no cover @overload def connect( @@ -372,8 +373,7 @@ def connect( unique: bool | str = ..., max_args: int | None = None, on_ref_error: RefErrorChoice = ..., - ) -> F: - ... # pragma: no cover + ) -> F: ... # pragma: no cover def connect( self, @@ -841,8 +841,7 @@ def emit( check_nargs: bool = False, check_types: bool = False, asynchronous: Literal[False] = False, - ) -> None: - ... # pragma: no cover + ) -> None: ... # pragma: no cover @overload def emit( @@ -948,8 +947,7 @@ def __call__( check_nargs: bool = False, check_types: bool = False, asynchronous: Literal[False] = False, - ) -> None: - ... # pragma: no cover + ) -> None: ... # pragma: no cover @overload def __call__( diff --git a/src/psygnal/_throttler.pyi b/src/psygnal/_throttler.pyi index 8714c133..c8155ff9 100644 --- a/src/psygnal/_throttler.pyi +++ b/src/psygnal/_throttler.pyi @@ -17,8 +17,10 @@ class _ThrottlerBase(Generic[P]): ) -> None: ... def cancel(self) -> None: """Cancel any pending calls.""" + def flush(self) -> None: """Force a call if there is one pending.""" + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> None: ... class Throttler(_ThrottlerBase[P]): diff --git a/src/psygnal/_weak_callback.py b/src/psygnal/_weak_callback.py index 58767803..2813895b 100644 --- a/src/psygnal/_weak_callback.py +++ b/src/psygnal/_weak_callback.py @@ -508,8 +508,7 @@ def dereference(self) -> partial | None: class SupportsSetitem(Protocol): - def __setitem__(self, key: Any, value: Any) -> None: - ... + def __setitem__(self, key: Any, value: Any) -> None: ... class WeakSetitem(WeakCallback): diff --git a/src/psygnal/containers/__init__.py b/src/psygnal/containers/__init__.py index da1fdb8e..b1a1ad78 100644 --- a/src/psygnal/containers/__init__.py +++ b/src/psygnal/containers/__init__.py @@ -1,4 +1,5 @@ """Containers backed by psygnal events.""" + from typing import TYPE_CHECKING, Any from ._evented_dict import EventedDict diff --git a/src/psygnal/containers/_evented_dict.py b/src/psygnal/containers/_evented_dict.py index ad504766..3e45c704 100644 --- a/src/psygnal/containers/_evented_dict.py +++ b/src/psygnal/containers/_evented_dict.py @@ -1,4 +1,5 @@ """Dict that emits events when altered.""" + from __future__ import annotations from typing import ( diff --git a/src/psygnal/containers/_evented_list.py b/src/psygnal/containers/_evented_list.py index a1a7c722..9fe08c52 100644 --- a/src/psygnal/containers/_evented_list.py +++ b/src/psygnal/containers/_evented_list.py @@ -21,6 +21,7 @@ MUST make sure that all the appropriate events are emitted. (Tests should cover this in test_evented_list.py) """ + from __future__ import annotations # pragma: no cover from typing import ( @@ -141,12 +142,10 @@ def insert(self, index: int, value: _T) -> None: self._post_insert(value) @overload - def __getitem__(self, key: int) -> _T: - ... + def __getitem__(self, key: int) -> _T: ... @overload - def __getitem__(self, key: slice) -> Self: - ... + def __getitem__(self, key: slice) -> Self: ... def __getitem__(self, key: Index) -> _T | Self: """Return self[key].""" @@ -154,12 +153,10 @@ def __getitem__(self, key: Index) -> _T | Self: return self.__newlike__(result) if isinstance(result, list) else result @overload - def __setitem__(self, key: int, value: _T) -> None: - ... + def __setitem__(self, key: int, value: _T) -> None: ... @overload - def __setitem__(self, key: slice, value: Iterable[_T]) -> None: - ... + def __setitem__(self, key: slice, value: Iterable[_T]) -> None: ... def __setitem__(self, key: Index, value: _T | Iterable[_T]) -> None: """Set self[key] to value.""" diff --git a/src/psygnal/containers/_evented_set.py b/src/psygnal/containers/_evented_set.py index 10f53f1b..10d624a0 100644 --- a/src/psygnal/containers/_evented_set.py +++ b/src/psygnal/containers/_evented_set.py @@ -73,14 +73,12 @@ def __repr__(self) -> str: def _pre_add_hook(self, item: _T) -> _T | BailType: return item # pragma: no cover - def _post_add_hook(self, item: _T) -> None: - ... # pragma: no cover + def _post_add_hook(self, item: _T) -> None: ... # pragma: no cover def _pre_discard_hook(self, item: _T) -> _T | BailType: return item # pragma: no cover - def _post_discard_hook(self, item: _T) -> None: - ... # pragma: no cover + def _post_discard_hook(self, item: _T) -> None: ... # pragma: no cover def _do_add(self, item: _T) -> None: self._data.add(item) diff --git a/src/psygnal/containers/_selectable_evented_list.py b/src/psygnal/containers/_selectable_evented_list.py index af05b255..d44ca695 100644 --- a/src/psygnal/containers/_selectable_evented_list.py +++ b/src/psygnal/containers/_selectable_evented_list.py @@ -1,4 +1,5 @@ """MutableSequence with a selection model.""" + from typing import Any, Iterable, Tuple, TypeVar from ._evented_list import EventedList, ListEvents diff --git a/src/psygnal/qt.py b/src/psygnal/qt.py index 61730a10..81805a00 100644 --- a/src/psygnal/qt.py +++ b/src/psygnal/qt.py @@ -5,6 +5,7 @@ psygnal is used in a Qt application, and you'd like to emit signals from a thread but have their callbacks invoked in the main thread. """ + from __future__ import annotations from threading import Thread, current_thread diff --git a/src/psygnal/utils.py b/src/psygnal/utils.py index 429a4c51..4254d7f6 100644 --- a/src/psygnal/utils.py +++ b/src/psygnal/utils.py @@ -1,4 +1,5 @@ """These utilities may help when using signals and evented objects.""" + from __future__ import annotations from contextlib import contextmanager diff --git a/tests/test_evented_decorator.py b/tests/test_evented_decorator.py index 71b8d468..4f527bf6 100644 --- a/tests/test_evented_decorator.py +++ b/tests/test_evented_decorator.py @@ -75,8 +75,7 @@ class Base: if decorator: @evented(equality_operators={"qux": operator.eq}) # just for test coverage - class Foo(Base): - ... + class Foo(Base): ... else: @@ -102,8 +101,7 @@ class Base: if decorator: @evented - class Foo(Base): - ... + class Foo(Base): ... else: @@ -135,8 +133,7 @@ class Base: if decorator: @evented - class Foo(Base): - ... + class Foo(Base): ... else: @@ -164,8 +161,7 @@ class Base(BaseModel): if decorator: @evented(events_namespace="my_events") - class Foo(Base): - ... + class Foo(Base): ... else: @@ -205,8 +201,7 @@ def test_no_signals_warn() -> None: with pytest.warns(UserWarning, match="No mutable fields found on class"): @evented - class Foo: - ... + class Foo: ... _ = Foo().events # type: ignore diff --git a/tests/test_evented_model.py b/tests/test_evented_model.py index 774206d5..aa4ec8e9 100644 --- a/tests/test_evented_model.py +++ b/tests/test_evented_model.py @@ -255,8 +255,7 @@ class Outer(EventedModel): def test_update_with_inner_model_protocol(): @runtime_checkable class InnerProtocol(Protocol): - def string(self) -> str: - ... + def string(self) -> str: ... # Protocol fields are not successfully set without explicit validation. @classmethod @@ -559,12 +558,10 @@ class M(EventedModel): x: int @property - def y(self): - ... + def y(self): ... @y.setter - def y(self, v): - ... + def y(self, v): ... if PYDANTIC_V2: model_config = { @@ -585,12 +582,10 @@ class M(EventedModel): x: int @property - def y(self): - ... + def y(self): ... @y.setter - def y(self, v): - ... + def y(self, v): ... if PYDANTIC_V2: model_config = { @@ -646,8 +641,7 @@ class Config: assert M(x=2).x == 2 - class N(M): - ... + class N(M): ... assert N(x=2).x == 2 diff --git a/tests/test_group.py b/tests/test_group.py index 66025e91..457506ef 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -188,8 +188,7 @@ def test_weakref(): """Make sure that the group doesn't keep a strong reference to the instance.""" import gc - class T: - ... + class T: ... obj = T() group = MyGroup(obj) @@ -203,8 +202,7 @@ def test_group_deepcopy(): from copy import deepcopy class T: - def method(self): - ... + def method(self): ... obj = T() group = MyGroup(obj) diff --git a/tests/test_psygnal.py b/tests/test_psygnal.py index 05536d1f..83fb845f 100644 --- a/tests/test_psygnal.py +++ b/tests/test_psygnal.py @@ -124,8 +124,7 @@ def boom(v: int): raise err @emitter.one_int.connect(check_nargs=False) - def bad_cb(a, b, c): - ... + def bad_cb(a, b, c): ... with pytest.raises(EmitLoopError) as e: emitter.one_int.emit(1) @@ -552,15 +551,13 @@ def test_checking_off(): # the no_check signal was instantiated with check_[nargs/types] = False @e.no_check.connect - def bad_in_many_ways(x: int, y, z): - ... + def bad_in_many_ways(x: int, y, z): ... def test_keyword_only_not_allowed(): e = Emitter() - def f(a: int, *, b: int): - ... + def f(a: int, *, b: int): ... with pytest.raises(ValueError) as er: e.two_int.connect(f) diff --git a/tests/test_qt_compat.py b/tests/test_qt_compat.py index 2d7aecbe..6678ed13 100644 --- a/tests/test_qt_compat.py +++ b/tests/test_qt_compat.py @@ -1,4 +1,5 @@ """qtbot should work for testing!""" + from threading import Thread, current_thread, main_thread from typing import TYPE_CHECKING, Any, Callable, Tuple from unittest.mock import Mock diff --git a/tests/test_weak_callable.py b/tests/test_weak_callable.py index e4b506f3..70eccf80 100644 --- a/tests/test_weak_callable.py +++ b/tests/test_weak_callable.py @@ -114,8 +114,7 @@ def test_weak_callable_equality() -> None: """Slot callers should be equal only if they represent the same bound-method.""" class T: - def x(self): - ... + def x(self): ... t1 = T() t2 = T() @@ -150,8 +149,7 @@ def test_nonreferencable() -> None: class T: __slots__ = ("x",) - def method(self) -> None: - ... + def method(self) -> None: ... t = T() with pytest.warns(UserWarning, match="failed to create weakref"): @@ -167,8 +165,7 @@ def method(self) -> None: @pytest.mark.parametrize("strong", [True, False]) def test_deref(strong: bool) -> None: - def func(x): - ... + def func(x): ... p = partial(func, 1) cb = weak_callback(p, strong_func=strong)