Skip to content

Commit 5f16508

Browse files
committed
refactor: middleware functions can now return None to cancel an action or event
1 parent 32f2c73 commit 5f16508

File tree

5 files changed

+38
-17
lines changed

5 files changed

+38
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Version 0.14.2
4+
5+
- refactor: middleware functions can now return `None` to cancel an action or event
6+
37
## Version 0.14.1
48

59
- feat: introduce `grace_time_in_seconds` parameter to `Store` to allow a grace

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "python-redux"
3-
version = "0.14.1"
3+
version = "0.14.2"
44
description = "Redux implementation for Python"
55
authors = ["Sassan Haradji <[email protected]>"]
66
license = "Apache-2.0"

redux/basic_types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ def __call__(
9797

9898

9999
class ActionMiddleware(Protocol, Generic[Action]):
100-
def __call__(self: ActionMiddleware, action: Action) -> Action: ...
100+
def __call__(self: ActionMiddleware, action: Action) -> Action | None: ...
101101

102102

103103
class EventMiddleware(Protocol, Generic[Event]):
104-
def __call__(self: EventMiddleware, event: Event) -> Event: ...
104+
def __call__(self: EventMiddleware, event: Event) -> Event | None: ...
105105

106106

107107
class CreateStoreOptions(Immutable, Generic[Action, Event]):

redux/main.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Store(Generic[State, Action, Event], SerializationMixin):
4545
"""Redux store for managing state and side effects."""
4646

4747
def __init__(
48-
self: Store[State, Action, Event],
48+
self: Store,
4949
reducer: ReducerType[State, Action, Event],
5050
options: CreateStoreOptions[Action, Event] | None = None,
5151
) -> None:
@@ -109,7 +109,12 @@ def _call_listeners(self: Store[State, Action, Event], state: State) -> None:
109109
self._create_task(result)
110110

111111
def _run_actions(self: Store[State, Action, Event]) -> None:
112-
action = self._actions.pop(0)
112+
while True:
113+
if len(self._actions) == 0:
114+
return
115+
action = self._actions.pop(0)
116+
if action is not None:
117+
break
113118
result = self.reducer(self._state, action)
114119
if is_complete_reducer_result(result):
115120
self._state = result.state
@@ -123,7 +128,12 @@ def _run_actions(self: Store[State, Action, Event]) -> None:
123128
self.dispatch(cast(Event, FinishEvent()))
124129

125130
def _run_event_handlers(self: Store[State, Action, Event]) -> None:
126-
event = self._events.pop(0)
131+
while True:
132+
if len(self._events) == 0:
133+
return
134+
event = self._events.pop(0)
135+
if event is not None:
136+
break
127137
for event_handler in self._event_handlers[type(event)].copy():
128138
self._event_handlers_queue.put_nowait((event_handler, event))
129139

@@ -139,6 +149,10 @@ def run(self: Store[State, Action, Event]) -> None:
139149

140150
def clean_up(self: Store[State, Action, Event]) -> None:
141151
"""Clean up the store."""
152+
self._event_handlers_queue.join()
153+
for _ in range(self.store_options.threads):
154+
self._event_handlers_queue.put_nowait(None)
155+
self._event_handlers_queue.join()
142156
for worker in self._workers:
143157
worker.join()
144158
self._workers.clear()
@@ -165,13 +179,21 @@ def dispatch(
165179
if isinstance(item, BaseAction):
166180
action = cast(Action, item)
167181
for action_middleware in self._action_middlewares:
168-
action = action_middleware(action)
169-
self._actions.append(action)
182+
action_ = action_middleware(action)
183+
if action_ is None:
184+
break
185+
action = action_
186+
else:
187+
self._actions.append(action)
170188
if isinstance(item, BaseEvent):
171189
event = cast(Event, item)
172190
for event_middleware in self._event_middlewares:
173-
event = event_middleware(event)
174-
self._events.append(event)
191+
event_ = event_middleware(event)
192+
if event_ is None:
193+
break
194+
event = event_
195+
else:
196+
self._events.append(event)
175197

176198
if self.store_options.scheduler is None and not self._is_running.locked():
177199
self.run()
@@ -226,19 +248,14 @@ def wait_for_store_to_finish(self: Store[State, Action, Event]) -> None:
226248
and self._event_handlers_queue.qsize() == 0
227249
):
228250
time.sleep(self.store_options.grace_time_in_seconds)
229-
self._event_handlers_queue.join()
230-
for _ in range(self.store_options.threads):
231-
self._event_handlers_queue.put_nowait(None)
232-
self._event_handlers_queue.join()
233251
self.clean_up()
234252
if self.store_options.on_finish:
235253
self.store_options.on_finish()
236254
break
237255
time.sleep(0.1)
238256

239257
def _handle_finish_event(self: Store[State, Action, Event]) -> None:
240-
thread = Thread(target=self.wait_for_store_to_finish)
241-
thread.start()
258+
Thread(target=self.wait_for_store_to_finish).start()
242259

243260
def autorun(
244261
self: Store[State, Action, Event],

redux/side_effect_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class SideEffectRunnerThread(threading.Thread, Generic[Event]):
2020
"""Thread for running side effects."""
2121

2222
def __init__(
23-
self: SideEffectRunnerThread[Event],
23+
self: SideEffectRunnerThread,
2424
*,
2525
task_queue: queue.Queue[tuple[EventHandler[Event], Event] | None],
2626
) -> None:

0 commit comments

Comments
 (0)