From 6d1a6907d2de9066cf4ec8f0104f66205f9ef6b3 Mon Sep 17 00:00:00 2001 From: DomNomNomVR <108088541+DomNomNomVR@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:09:42 +1300 Subject: [PATCH] changes from voice chat --- gears/__init__.py | 3 +- gears/connections.py | 5 +-- gears/effect.py | 15 ++++--- gears/gear.py | 8 ++-- gears/wrap_aware_tuple.py | 90 --------------------------------------- tests/test_signals.py | 4 +- 6 files changed, 19 insertions(+), 106 deletions(-) delete mode 100644 gears/wrap_aware_tuple.py diff --git a/gears/__init__.py b/gears/__init__.py index 2a40261..c98a144 100644 --- a/gears/__init__.py +++ b/gears/__init__.py @@ -1 +1,2 @@ -from .gear import * +from .gear import Gear +from .effect import Effect diff --git a/gears/connections.py b/gears/connections.py index cafc10a..c73f812 100644 --- a/gears/connections.py +++ b/gears/connections.py @@ -3,11 +3,8 @@ from typing import Any, Hashable, TypeVar, Callable from gears.effect import effect_of from gears.gear import Gear -S = TypeVar("S", bound=Hashable) -T = TypeVar("T", bound=Hashable) - -def connect( +def connect[S: Hashable, T: Hashable]( source: Gear[S], to_target_val: Callable[[S], T], to_source: Callable[[S, T], S], diff --git a/gears/effect.py b/gears/effect.py index f311dec..165e13a 100644 --- a/gears/effect.py +++ b/gears/effect.py @@ -1,8 +1,8 @@ +from __future__ import annotations from typing import Any, Callable, cast - -from gears import Gear from .wrap_aware_tuple import WrapAwareTuple + class Effect[*Ts]: def __init__(self, fn: Callable[[*Ts], None], gears: WrapAwareTuple[Gear, *Ts]): for gear in gears: @@ -15,17 +15,22 @@ class Effect[*Ts]: def on_change(self): self._fn(*self._gears.unwrap(lambda x: x())) -def effect_of[*Ts, *G_Ts]( - *gears: Gear[Any] + +def effect_of[*Ts]( + *gears: Gear[Any], ) -> Callable[[Callable[[*Ts], None]], Effect]: # No deduction, no tears, only cast now ts_gears = cast(WrapAwareTuple[Gear, *Ts], WrapAwareTuple.prewrapped(gears)) return effect_of_typechecked(ts_gears) + def effect_of_typechecked[*Ts]( - gears: WrapAwareTuple[Gear, *Ts] + gears: WrapAwareTuple[Gear, *Ts], ) -> Callable[[Callable[[*Ts], None]], Effect]: def decorator(fn: Callable[[*Ts], None]) -> Effect: return Effect(fn, gears) return decorator + + +from gears import Gear diff --git a/gears/gear.py b/gears/gear.py index 5ecee8b..840b215 100644 --- a/gears/gear.py +++ b/gears/gear.py @@ -1,14 +1,14 @@ +from __future__ import annotations from typing import Any, Hashable, TypeVar, TYPE_CHECKING -T = TypeVar("T", bound=Hashable) - if TYPE_CHECKING: from gears.effect import Effect -class Gear[T](): + +class Gear[T: Hashable]: def __init__(self, value: T): self._value = value - self.effects: list['Effect'] = [] + self.effects: list[Effect] = [] def get(self) -> T: return self._value diff --git a/gears/wrap_aware_tuple.py b/gears/wrap_aware_tuple.py deleted file mode 100644 index 36eb1f2..0000000 --- a/gears/wrap_aware_tuple.py +++ /dev/null @@ -1,90 +0,0 @@ - -from typing import cast, Callable, reveal_type, TYPE_CHECKING - -# no-op "Wrap" type -type Identity[X] = X - -class WrapAwareTuple[Wrap, *Ts](tuple[Wrap, ...]): - ''' - Wrap is a wrapper type around each of the types in Ts, in order. - - For example: - - a: Wrapped[Wrap, int, str, float] = Wrapped(Wrap[int], Wrap[str], Wrap[float]) - - This lets us keep track of type transformations, starting from the Identity mapping. - ''' - - @staticmethod - def id[*NewTs](*args: *NewTs) -> 'WrapAwareTuple[Identity, *NewTs]': - ''' - Create a new WrapAwareTuple, using the Identity psuedo-wrapper. - ''' - return WrapAwareTuple[Identity, *NewTs](args) - - @staticmethod - def prewrapped[NewWrap, *NewTs](args: tuple[NewWrap, ...]) -> 'WrapAwareTuple[NewWrap, *NewTs]': - ''' - Create a new WrapAwareTuple, using the given wrapper type. - ''' - return WrapAwareTuple[NewWrap, *NewTs](args) - - def wrap[NewWrap](self, mapper: Callable[[Wrap], NewWrap]) -> 'WrapAwareTuple[NewWrap, *Ts]': - ''' - Wrap all existing elements with a new wrapping function. - - This function must unwrap the current wrapping type, if non-Identity, and apply a new one. - - To pop the wrapped value from a tuple wrapper: - >>> Wrapped[tuple, int](3).wrap[Identity](lambda x: x[0]) - - To wrap an unwrapped value into a tuple: - >>> Wrapped[Identity, int](3).wrap(tuple) - ''' - # pyright supports Union[*Ts], but we still can't do applicative functor things with it - return WrapAwareTuple[NewWrap, *Ts](mapper(v) for v in self) - - def unwrap(self, mapper: Callable[[Wrap], Identity]) -> tuple[*Ts]: - ''' - Unwrap all elements in the tuple, returning a normal tuple. - - This is the same as calling wrap with the appropriate wrapper, except we simplify the signature and - return type for this case. - ''' - return cast(tuple[*Ts], self.wrap(mapper)) - -def list_fmap[*Ts](t: tuple[*Ts]): - match t: - case (head, *tail): - return ([head], *list_fmap(tuple(tail))) - case _: - return () - - -if TYPE_CHECKING: - def _fmap_scratchpad(): - reveal_type(list_fmap((1,"s",3.0))) - # Type of "list_fmap((1, "s", 3))" is "tuple[list[*tuple[int, str, float]], *tuple[list[*tuple[Unknown, ...]] | Unknown, ...]] | tuple[()]" - # ...wanted to get "tuple[list[int], list[str], list[float]]"; RIP - - def _wrapaware_scratchpad(): - - atom = WrapAwareTuple.id(3) - reveal_type(atom) - # Type of "atom" is "Wrapped[Unknown, int]" - # (variable) atom: Wrapped[Identity[Unknown], int] - - compound_3 = WrapAwareTuple.id(3, "str", 9.0) - reveal_type(compound_3) - # Type of "compound_3" is "Wrapped[Unknown, int, str, float]" - # (variable) compound_3: Wrapped[Identity[Unknown], int, str, float] - - tupled_compound_3 = compound_3.wrap(tuple) - reveal_type(tupled_compound_3) - # Type of "tupled_compound_3" is "Wrapped[tuple[Unknown, ...], int, str, float]" - # (variable) tupled_compound_3: Wrapped[tuple[Identity[Unknown], ...], int, str, float] - - restored_compound_3 = tupled_compound_3.wrap(lambda x: x[0]) - reveal_type(restored_compound_3) - # Type of "restored_compound_3" is "Wrapped[Unknown, int, str, float]" - # (variable) restored_compound_3: Wrapped[Unknown, int, str, float] diff --git a/tests/test_signals.py b/tests/test_signals.py index 0aee19c..5b7af55 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -3,7 +3,7 @@ from typing import NamedTuple from unittest.mock import MagicMock from gears import Gear from gears.connections import connect -from gears.effect import effect_of +from gears.effect import effect_of, effect_of_2 def test_get_set(): @@ -35,7 +35,7 @@ def test_effect_of_2(): last_arg: tuple[str, int] | None = None - @effect_of(g1, g2) + @effect_of_2(g1, g2) def value_changed(v1: str, v2: int): nonlocal last_arg last_arg = (v1, v2)