Skip to content

Commit 822af83

Browse files
committed
refactor: encapsulate autorun options previously provided as multiple keyword arguments, in a single AutorunOptions immutable class
refactor: rename `immediate` to `immediate_run` in autorun subscribers feat: default value of `immediate_run` can be set for all subscribers of an autorun instance by settings `subscribers_immediate_run` option for the autorun
1 parent 2a0c107 commit 822af83

File tree

6 files changed

+99
-63
lines changed

6 files changed

+99
-63
lines changed

CHANGELOG.md

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

3+
## Version 0.9.20
4+
5+
- refactor: encapsulate autorun options previously provided as multiple keyword arguments,
6+
in a single `AutorunOptions` immutable class
7+
- refactor: rename `immediate` to `immediate_run` in autorun subscribers
8+
- feat: default value of `immediate_run` can be set for all subscribers of an autorun
9+
instance by settings `subscribers_immediate_run` option for the autorun
10+
311
## Version 0.9.19
412

513
- feat: add `immediate` parameter to `subscribe` method of `autorun`'s returned value

demo.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,13 @@ def main() -> None:
127127
# Initialization <
128128
store = create_store(
129129
reducer,
130-
CreateStoreOptions(autorun_initial_run=True),
130+
CreateStoreOptions(auto_init=True, threads=2),
131131
)
132132

133133
def event_handler(event: SleepEvent) -> None:
134134
time.sleep(event.duration)
135135

136136
store.subscribe_event(SleepEvent, event_handler)
137-
138-
store.dispatch(InitAction())
139137
# >
140138

141139
# -----

poetry.lock

Lines changed: 25 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "python-redux"
3-
version = "0.9.19"
3+
version = "0.9.20"
44
description = "Redux implementation for Python"
55
authors = ["Sassan Haradji <sassanh@gmail.com>"]
66
license = "Apache-2.0"
@@ -11,6 +11,7 @@ packages = [{ include = "redux" }]
1111
python = "^3.9"
1212
python-immutable = "^1.0.0"
1313
typing-extensions = "^4.9.0"
14+
pyright = "^1.1.348"
1415

1516

1617
[tool.poetry.scripts]
@@ -22,7 +23,7 @@ optional = true
2223

2324
[tool.poetry.group.dev.dependencies]
2425
poethepoet = "^0.24.3"
25-
pyright = "^1.1.342"
26+
pyright = "^1.1.348"
2627
ruff = "^0.1.9"
2728

2829
[build-system]
@@ -46,3 +47,6 @@ multiline-quotes = "double"
4647

4748
[tool.ruff.format]
4849
quote-style = 'single'
50+
51+
[tool.isort]
52+
profile = "black"

redux/basic_types.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ruff: noqa: A003, D100, D101, D102, D103, D104, D105, D107
22
from __future__ import annotations
33

4-
from typing import Any, Callable, Generic, Protocol, Sequence, TypeAlias, TypeGuard
4+
from typing import Any, Callable, Generic, Protocol, TypeAlias, TypeGuard
55

66
from immutable import Immutable
77
from typing_extensions import TypeVar
@@ -26,21 +26,20 @@ class EventSubscriptionOptions(Immutable):
2626
Event2 = TypeVar('Event2', bound=BaseEvent, infer_variance=True)
2727
SelectorOutput = TypeVar('SelectorOutput', infer_variance=True)
2828
ComparatorOutput = TypeVar('ComparatorOutput', infer_variance=True)
29+
AutorunOriginalReturnType = TypeVar('AutorunOriginalReturnType', infer_variance=True)
2930
Comparator = Callable[[State], ComparatorOutput]
3031
EventHandler = Callable[[Event], Any] | Callable[[], Any]
3132

3233

3334
class CompleteReducerResult(Immutable, Generic[State, Action, Event]):
3435
state: State
35-
actions: Sequence[Action] | None = None
36-
events: Sequence[Event] | None = None
36+
actions: list[Action] | None = None
37+
events: list[Event] | None = None
3738

3839

3940
ReducerResult = CompleteReducerResult[State, Action, Event] | State
4041
ReducerType = Callable[[State | None, Action], ReducerResult[State, Action, Event]]
4142

42-
AutorunOriginalReturnType = TypeVar('AutorunOriginalReturnType', infer_variance=True)
43-
4443

4544
class InitializationActionError(Exception):
4645
def __init__(self: InitializationActionError, action: BaseAction) -> None:
@@ -80,27 +79,42 @@ def __call__(self: Scheduler, callback: Callable, *, interval: bool) -> None:
8079
class CreateStoreOptions(Immutable):
8180
auto_init: bool = False
8281
threads: int = 5
83-
autorun_initial_run: bool = True
8482
scheduler: Scheduler | None = None
8583
action_middleware: Callable[[BaseAction], Any] | None = None
8684
event_middleware: Callable[[BaseEvent], Any] | None = None
8785

8886

87+
CreStoreOptions = CreateStoreOptions(auto_init=True)
88+
89+
90+
class AutorunOptions(Immutable, Generic[AutorunOriginalReturnType]):
91+
default_value: AutorunOriginalReturnType | None = None
92+
initial_run: bool = True
93+
subscribers_immediate_run: bool = True
94+
95+
8996
class AutorunType(Protocol, Generic[State]):
9097
def __call__(
9198
self: AutorunType,
9299
selector: Callable[[State], SelectorOutput],
93100
comparator: Callable[[State], Any] | None = None,
94101
*,
95-
default_value: AutorunOriginalReturnType | None = None,
96-
initial_run: bool = True,
97-
) -> AutorunDecorator[State, SelectorOutput, AutorunOriginalReturnType]:
102+
options: AutorunOptions[AutorunOriginalReturnType] | None = None,
103+
) -> AutorunDecorator[
104+
State,
105+
SelectorOutput,
106+
AutorunOriginalReturnType,
107+
]:
98108
...
99109

100110

101111
class AutorunDecorator(
102112
Protocol,
103-
Generic[State, SelectorOutput, AutorunOriginalReturnType],
113+
Generic[
114+
State,
115+
SelectorOutput,
116+
AutorunOriginalReturnType,
117+
],
104118
):
105119
def __call__(
106120
self: AutorunDecorator,
@@ -110,7 +124,10 @@ def __call__(
110124
...
111125

112126

113-
class AutorunReturnType(Protocol, Generic[AutorunOriginalReturnType]):
127+
class AutorunReturnType(
128+
Protocol,
129+
Generic[AutorunOriginalReturnType],
130+
):
114131
def __call__(self: AutorunReturnType) -> AutorunOriginalReturnType:
115132
...
116133

@@ -122,7 +139,7 @@ def subscribe(
122139
self: AutorunReturnType,
123140
callback: Callable[[AutorunOriginalReturnType], Any],
124141
*,
125-
immediate: bool = False,
142+
immediate_run: bool | None = None,
126143
) -> Callable[[], None]:
127144
...
128145

@@ -150,7 +167,10 @@ def __call__(
150167
...
151168

152169

153-
class InitializeStateReturnValue(Immutable, Generic[State, Action, Event]):
170+
class InitializeStateReturnValue(
171+
Immutable,
172+
Generic[State, Action, Event],
173+
):
154174
dispatch: Dispatch[State, Action, Event]
155175
subscribe: Callable[[Callable[[State], Any]], Callable[[], None]]
156176
subscribe_event: EventSubscriber

redux/main.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .basic_types import (
1212
Action,
1313
AutorunDecorator,
14+
AutorunOptions,
1415
AutorunOriginalReturnType,
1516
AutorunReturnType,
1617
BaseAction,
@@ -63,7 +64,7 @@ def create_store(
6364
reducer: ReducerType[State, Action, Event],
6465
options: CreateStoreOptions | None = None,
6566
) -> InitializeStateReturnValue[State, Action, Event]:
66-
_options = CreateStoreOptions() if options is None else options
67+
store_options = options or CreateStoreOptions()
6768

6869
state: State | None = None
6970
listeners: set[Callable[[State], Any]] = set()
@@ -76,7 +77,7 @@ def create_store(
7677
events: list[Event] = []
7778

7879
event_handlers_queue = queue.Queue[tuple[EventHandler[Event], Event] | None]()
79-
for _ in range(_options.threads):
80+
for _ in range(store_options.threads):
8081
worker = SideEffectRunnerThread(event_handlers_queue)
8182
worker.start()
8283

@@ -128,15 +129,15 @@ def dispatch(
128129

129130
for item in items:
130131
if isinstance(item, BaseAction):
131-
if _options.action_middleware:
132-
_options.action_middleware(item)
132+
if store_options.action_middleware:
133+
store_options.action_middleware(item)
133134
actions.append(item)
134135
if isinstance(item, BaseEvent):
135-
if _options.event_middleware:
136-
_options.event_middleware(item)
136+
if store_options.event_middleware:
137+
store_options.event_middleware(item)
137138
events.append(item)
138139

139-
if _options.scheduler is None and not is_running.locked():
140+
if store_options.scheduler is None and not is_running.locked():
140141
run()
141142

142143
def subscribe(listener: Callable[[State], Any]) -> Callable[[], None]:
@@ -155,7 +156,7 @@ def subscribe_event(
155156
)
156157

157158
def handle_finish_event(_event: Event) -> None:
158-
for _ in range(_options.threads):
159+
for _ in range(store_options.threads):
159160
event_handlers_queue.put(None)
160161

161162
subscribe_event(cast(type[Event], FinishEvent), handle_finish_event)
@@ -164,9 +165,13 @@ def autorun(
164165
selector: Callable[[State], SelectorOutput],
165166
comparator: Callable[[State], ComparatorOutput] | None = None,
166167
*,
167-
default_value: AutorunOriginalReturnType | None = None,
168-
initial_run: bool = _options.autorun_initial_run,
169-
) -> AutorunDecorator[State, SelectorOutput, AutorunOriginalReturnType]:
168+
options: AutorunOptions[AutorunOriginalReturnType] | None = None,
169+
) -> AutorunDecorator[
170+
State,
171+
SelectorOutput,
172+
AutorunOriginalReturnType,
173+
]:
174+
autorun_options = options or AutorunOptions()
170175
nonlocal state
171176

172177
def decorator(
@@ -175,7 +180,7 @@ def decorator(
175180
) -> AutorunReturnType[AutorunOriginalReturnType]:
176181
last_selector_result: SelectorOutput | None = None
177182
last_comparator_result: ComparatorOutput = cast(ComparatorOutput, object())
178-
last_value: AutorunOriginalReturnType | None = default_value
183+
last_value: AutorunOriginalReturnType | None = autorun_options.default_value
179184
subscriptions: list[Callable[[AutorunOriginalReturnType], Any]] = []
180185

181186
def check_and_call(state: State) -> None:
@@ -215,7 +220,7 @@ def check_and_call(state: State) -> None:
215220
for subscriber in subscriptions:
216221
subscriber(last_value)
217222

218-
if initial_run and state is not None:
223+
if autorun_options.initial_run and state is not None:
219224
check_and_call(state)
220225

221226
subscribe(check_and_call)
@@ -234,11 +239,12 @@ def subscribe(
234239
self: Call,
235240
callback: Callable[[AutorunOriginalReturnType], Any],
236241
*,
237-
immediate: bool = False,
242+
immediate_run: bool
243+
| None = autorun_options.subscribers_immediate_run,
238244
) -> Callable[[], None]:
239245
subscriptions.append(callback)
240246

241-
if immediate:
247+
if immediate_run:
242248
callback(self.value)
243249

244250
def unsubscribe() -> None:
@@ -250,17 +256,17 @@ def unsubscribe() -> None:
250256

251257
return decorator
252258

253-
if _options.auto_init:
254-
if _options.scheduler:
255-
_options.scheduler(
259+
if store_options.auto_init:
260+
if store_options.scheduler:
261+
store_options.scheduler(
256262
lambda: dispatch(cast(Action, InitAction())),
257263
interval=False,
258264
)
259265
else:
260266
dispatch(cast(Action, InitAction()))
261267

262-
if _options.scheduler:
263-
_options.scheduler(run, interval=True)
268+
if store_options.scheduler:
269+
store_options.scheduler(run, interval=True)
264270

265271
return InitializeStateReturnValue(
266272
dispatch=dispatch,

0 commit comments

Comments
 (0)