11# ruff: noqa: D100, D101, D102, D103, D104, D105, D107
22from __future__ import annotations
33
4+ import inspect
45import weakref
6+ from asyncio import iscoroutinefunction
57from inspect import signature
6- from types import MethodType
7- from typing import TYPE_CHECKING , Any , Callable , Generic , cast
8+ from typing import TYPE_CHECKING , Any , Callable , Coroutine , Generic , cast
89
910from redux .basic_types import (
1011 Action ,
1718)
1819
1920if TYPE_CHECKING :
21+ from types import MethodType
22+
2023 from redux .main import Store
2124
2225
@@ -43,30 +46,104 @@ def __init__( # noqa: PLR0913
4346 self ._store = store
4447 self ._selector = selector
4548 self ._comparator = comparator
46- self ._func = func
49+ if options .keep_ref :
50+ self ._func = func
51+ elif inspect .ismethod (func ):
52+ self ._func = weakref .WeakMethod (func )
53+ else :
54+ self ._func = weakref .ref (func )
4755 self ._options = options
4856
4957 self ._last_selector_result : SelectorOutput | None = None
5058 self ._last_comparator_result : ComparatorOutput = cast (
5159 ComparatorOutput ,
5260 object (),
5361 )
54- self ._latest_value : AutorunOriginalReturnType | None = options .default_value
62+ self ._latest_value : AutorunOriginalReturnType = options .default_value
5563 self ._subscriptions : set [
5664 Callable [[AutorunOriginalReturnType ], Any ]
5765 | weakref .ref [Callable [[AutorunOriginalReturnType ], Any ]]
5866 ] = set ()
67+ self ._immediate_run = (
68+ not iscoroutinefunction (func )
69+ if options .subscribers_immediate_run is None
70+ else options .subscribers_immediate_run
71+ )
5972
6073 if self ._options .initial_run and store ._state is not None : # noqa: SLF001
61- self .check_and_call (store ._state ) # noqa: SLF001
74+ self ._check_and_call (store ._state ) # noqa: SLF001
6275
63- store .subscribe (self .check_and_call )
76+ store .subscribe (self ._check_and_call )
6477
65- def check_and_call (self : Autorun , state : State ) -> None :
78+ def inform_subscribers (
79+ self : Autorun [
80+ State ,
81+ Action ,
82+ Event ,
83+ SelectorOutput ,
84+ ComparatorOutput ,
85+ AutorunOriginalReturnType ,
86+ ],
87+ ) -> None :
88+ for subscriber_ in self ._subscriptions .copy ():
89+ if isinstance (subscriber_ , weakref .ref ):
90+ subscriber = subscriber_ ()
91+ if subscriber is None :
92+ self ._subscriptions .discard (subscriber_ )
93+ continue
94+ else :
95+ subscriber = subscriber_
96+ subscriber (self ._latest_value )
97+
98+ def call_func (
99+ self : Autorun [
100+ State ,
101+ Action ,
102+ Event ,
103+ SelectorOutput ,
104+ ComparatorOutput ,
105+ AutorunOriginalReturnType ,
106+ ],
107+ selector_result : SelectorOutput ,
108+ previous_result : SelectorOutput | None ,
109+ func : Callable [
110+ [SelectorOutput , SelectorOutput ],
111+ AutorunOriginalReturnType ,
112+ ]
113+ | Callable [[SelectorOutput ], AutorunOriginalReturnType ]
114+ | MethodType ,
115+ ) -> AutorunOriginalReturnType :
116+ if len (signature (func ).parameters ) == 1 :
117+ return cast (
118+ Callable [[SelectorOutput ], AutorunOriginalReturnType ],
119+ func ,
120+ )(selector_result )
121+ return cast (
122+ Callable [
123+ [SelectorOutput , SelectorOutput | None ],
124+ AutorunOriginalReturnType ,
125+ ],
126+ func ,
127+ )(selector_result , previous_result )
128+
129+ def _check_and_call (
130+ self : Autorun [
131+ State ,
132+ Action ,
133+ Event ,
134+ SelectorOutput ,
135+ ComparatorOutput ,
136+ AutorunOriginalReturnType ,
137+ ],
138+ state : State ,
139+ ) -> None :
66140 try :
67141 selector_result = self ._selector (state )
68142 except AttributeError :
69143 return
144+ func = self ._func () if isinstance (self ._func , weakref .ref ) else self ._func
145+ if func is None :
146+ return
70147 if self ._comparator is None :
71148 comparator_result = cast (ComparatorOutput , selector_result )
72149 else :
@@ -75,31 +152,11 @@ def check_and_call(self: Autorun, state: State) -> None:
75152 previous_result = self ._last_selector_result
76153 self ._last_selector_result = selector_result
77154 self ._last_comparator_result = comparator_result
78- if len (signature (self ._func ).parameters ) == 1 :
79- self ._latest_value = cast (
80- Callable [[SelectorOutput ], AutorunOriginalReturnType ],
81- self ._func ,
82- )(selector_result )
155+ self ._latest_value = self .call_func (selector_result , previous_result , func )
156+ if self ._immediate_run :
157+ self .inform_subscribers ()
83158 else :
84- self ._latest_value = cast (
85- Callable [
86- [SelectorOutput , SelectorOutput | None ],
87- AutorunOriginalReturnType ,
88- ],
89- self ._func ,
90- )(
91- selector_result ,
92- previous_result ,
93- )
94- for subscriber_ in self ._subscriptions .copy ():
95- if isinstance (subscriber_ , weakref .ref ):
96- subscriber = subscriber_ ()
97- if subscriber is None :
98- self ._subscriptions .discard (subscriber_ )
99- continue
100- else :
101- subscriber = subscriber_
102- subscriber (self ._latest_value )
159+ self ._store ._create_task (cast (Coroutine , self ._latest_value )) # noqa: SLF001
103160
104161 def __call__ (
105162 self : Autorun [
@@ -112,7 +169,7 @@ def __call__(
112169 ],
113170 ) -> AutorunOriginalReturnType :
114171 if self ._store ._state is not None : # noqa: SLF001
115- self .check_and_call (self ._store ._state ) # noqa: SLF001
172+ self ._check_and_call (self ._store ._state ) # noqa: SLF001
116173 return cast (AutorunOriginalReturnType , self ._latest_value )
117174
118175 def __repr__ (
@@ -161,7 +218,7 @@ def subscribe(
161218 keep_ref = self ._options .subscribers_keep_ref
162219 if keep_ref :
163220 callback_ref = callback
164- elif isinstance (callback , MethodType ):
221+ elif inspect . ismethod (callback ):
165222 callback_ref = weakref .WeakMethod (callback )
166223 else :
167224 callback_ref = weakref .ref (callback )
0 commit comments