Skip to content

Commit a793d58

Browse files
committed
refactor: use match statement instead of isinstance chains in reducers
Replace all if/isinstance type-dispatch chains with Python's structural pattern matching (match statement) in demos, tests, and README.md.
1 parent c71bfb5 commit a793d58

18 files changed

Lines changed: 326 additions & 249 deletions

CHANGELOG.md

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

3+
## Unreleased
4+
5+
- refactor: replace `if isinstance(action, ...)` chains with Python `match` statement in all reducers across demos, tests, and README
6+
37
## Version 0.25.4
48

59
- fix: add missing `redux_pytest` packages to binary wheels in github actions

README.md

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,34 +75,36 @@ def reducer(
7575
),
7676
],
7777
)
78-
if isinstance(action, AddTodoItemAction):
79-
return replace(
80-
state,
81-
items=[
82-
*state.items,
83-
ToDoItem(
84-
id=uuid.uuid4().hex,
85-
content=action.content,
86-
),
87-
],
88-
)
89-
if isinstance(action, RemoveTodoItemAction):
90-
return replace(
91-
state,
92-
actions=[item for item in state.items if item.id != action.id],
93-
)
94-
if isinstance(action, MarkTodoItemDone):
95-
return CompleteReducerResult(
96-
state=replace(
78+
match action:
79+
case AddTodoItemAction():
80+
return replace(
9781
state,
9882
items=[
99-
replace(item, is_done=True) if item.id == action.id else item
100-
for item in state.items
83+
*state.items,
84+
ToDoItem(
85+
id=uuid.uuid4().hex,
86+
content=action.content,
87+
),
10188
],
102-
),
103-
events=[CallApi(parameters={})],
104-
)
105-
return state
89+
)
90+
case RemoveTodoItemAction():
91+
return replace(
92+
state,
93+
actions=[item for item in state.items if item.id != action.id],
94+
)
95+
case MarkTodoItemDone():
96+
return CompleteReducerResult(
97+
state=replace(
98+
state,
99+
items=[
100+
replace(item, is_done=True) if item.id == action.id else item
101+
for item in state.items
102+
],
103+
),
104+
events=[CallApi(parameters={})],
105+
)
106+
case _:
107+
return state
106108

107109

108110
store = Store(reducer)
@@ -154,7 +156,7 @@ store.dispatch(FinishAction())
154156
- Reduce boilerplate by dropping `type` property, payload classes and action creators:
155157

156158
- Each action is a subclass of `BaseAction`.
157-
- Its type is checked by utilizing `isinstance` (no need for `type` property).
159+
- Its type is checked by using Python's `match` statement (no need for `type` property).
158160
- Its payload are its direct properties (no need for a separate `payload` object).
159161
- Its creator is its auto-generated constructor.
160162

demo.py

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,37 @@ def straight_reducer(
5656
action: ActionType,
5757
) -> CountStateType:
5858
if state is None:
59-
if isinstance(action, InitAction):
60-
return CountStateType(count=0)
61-
raise InitializationActionError(action)
62-
if isinstance(action, IncrementAction):
63-
return CountStateType(count=state.count + 1)
64-
if isinstance(action, DecrementAction):
65-
return CountStateType(count=state.count - 1)
66-
return state
59+
match action:
60+
case InitAction():
61+
return CountStateType(count=0)
62+
case _:
63+
raise InitializationActionError(action)
64+
match action:
65+
case IncrementAction():
66+
return CountStateType(count=state.count + 1)
67+
case DecrementAction():
68+
return CountStateType(count=state.count - 1)
69+
case _:
70+
return state
6771

6872

6973
def base10_reducer(
7074
state: CountStateType | None,
7175
action: ActionType,
7276
) -> CountStateType:
7377
if state is None:
74-
if isinstance(action, InitAction):
75-
return CountStateType(count=10)
76-
raise InitializationActionError(action)
77-
if isinstance(action, IncrementAction):
78-
return CountStateType(count=state.count + 1)
79-
if isinstance(action, DecrementAction):
80-
return CountStateType(count=state.count - 1)
81-
return state
78+
match action:
79+
case InitAction():
80+
return CountStateType(count=10)
81+
case _:
82+
raise InitializationActionError(action)
83+
match action:
84+
case IncrementAction():
85+
return CountStateType(count=state.count + 1)
86+
case DecrementAction():
87+
return CountStateType(count=state.count - 1)
88+
case _:
89+
return state
8290

8391

8492
class SleepEvent(BaseEvent):
@@ -90,20 +98,24 @@ def inverse_reducer(
9098
action: ActionType,
9199
) -> ReducerResult[CountStateType, ActionType, SleepEvent]:
92100
if state is None:
93-
if isinstance(action, InitAction):
94-
return CountStateType(count=0)
95-
raise InitializationActionError(action)
96-
if isinstance(action, IncrementAction):
97-
return CountStateType(count=state.count - 1)
98-
if isinstance(action, DecrementAction):
99-
return CountStateType(count=state.count + 1)
100-
if isinstance(action, DoNothingAction):
101-
return CompleteReducerResult(
102-
state=state,
103-
actions=[IncrementAction()],
104-
events=[SleepEvent(duration=3)],
105-
)
106-
return state
101+
match action:
102+
case InitAction():
103+
return CountStateType(count=0)
104+
case _:
105+
raise InitializationActionError(action)
106+
match action:
107+
case IncrementAction():
108+
return CountStateType(count=state.count - 1)
109+
case DecrementAction():
110+
return CountStateType(count=state.count + 1)
111+
case DoNothingAction():
112+
return CompleteReducerResult(
113+
state=state,
114+
actions=[IncrementAction()],
115+
events=[SleepEvent(duration=3)],
116+
)
117+
case _:
118+
return state
107119

108120

109121
reducer, reducer_id = combine_reducers(

tests/test_async.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,22 @@ def reducer(
4848
action: Action,
4949
) -> StateType | CompleteReducerResult[StateType, Action, IncrementEvent]:
5050
if state is None:
51-
if isinstance(action, InitAction):
52-
return StateType(value=0, mirrored_value=0)
53-
raise InitializationActionError(action)
54-
55-
if isinstance(action, IncrementAction):
56-
return CompleteReducerResult(
57-
state=replace(state, value=state.value + 1),
58-
events=[IncrementEvent(post_value=state.value + 1)],
59-
)
60-
if isinstance(action, SetMirroredValueAction):
61-
return replace(state, mirrored_value=action.value)
62-
return state
51+
match action:
52+
case InitAction():
53+
return StateType(value=0, mirrored_value=0)
54+
case _:
55+
raise InitializationActionError(action)
56+
57+
match action:
58+
case IncrementAction():
59+
return CompleteReducerResult(
60+
state=replace(state, value=state.value + 1),
61+
events=[IncrementEvent(post_value=state.value + 1)],
62+
)
63+
case SetMirroredValueAction():
64+
return replace(state, mirrored_value=action.value)
65+
case _:
66+
return state
6367

6468

6569
Action = IncrementAction | SetMirroredValueAction | InitAction | FinishAction

tests/test_autorun.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,21 @@ def reducer(
5151
action: Action,
5252
) -> ReducerResult[StateType, Action, FinishEvent]:
5353
if state is None:
54-
if isinstance(action, InitAction):
55-
return StateType(value=0)
56-
raise InitializationActionError(action)
57-
58-
if isinstance(action, IncrementAction):
59-
return replace(state, value=state.value + 1)
60-
61-
if isinstance(action, DecrementAction):
62-
return replace(state, value=state.value - 1)
63-
64-
if isinstance(action, IncrementByTwoAction):
65-
return replace(state, value=state.value + 2)
66-
67-
return state
54+
match action:
55+
case InitAction():
56+
return StateType(value=0)
57+
case _:
58+
raise InitializationActionError(action)
59+
60+
match action:
61+
case IncrementAction():
62+
return replace(state, value=state.value + 1)
63+
case DecrementAction():
64+
return replace(state, value=state.value - 1)
65+
case IncrementByTwoAction():
66+
return replace(state, value=state.value + 2)
67+
case _:
68+
return state
6869

6970

7071
StoreType = Store[StateType, Action, FinishEvent]

tests/test_combinations.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,21 @@ def reducer(
3636
action: Action,
3737
) -> StateType | CompleteReducerResult[StateType, Action, FinishEvent]:
3838
if state is None:
39-
if isinstance(action, InitAction):
40-
return StateType(value1=0, value2=0)
41-
raise InitializationActionError(action)
42-
43-
if isinstance(action, IncrementAction):
44-
field_name = f'value{action.which}'
45-
return replace(
46-
state,
47-
**{field_name: getattr(state, field_name) + 1},
48-
)
49-
50-
return state
39+
match action:
40+
case InitAction():
41+
return StateType(value1=0, value2=0)
42+
case _:
43+
raise InitializationActionError(action)
44+
45+
match action:
46+
case IncrementAction():
47+
field_name = f'value{action.which}'
48+
return replace(
49+
state,
50+
**{field_name: getattr(state, field_name) + 1},
51+
)
52+
case _:
53+
return state
5154

5255

5356
StoreType = Store[StateType, Action, FinishEvent]

tests/test_custom_autorun.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ def call(
4040

4141
def reducer(state: State | None, action: BaseAction) -> State:
4242
if state is None:
43-
if isinstance(action, InitAction):
44-
return State(value=0)
45-
raise InitializationActionError(action)
43+
match action:
44+
case InitAction():
45+
return State(value=0)
46+
case _:
47+
raise InitializationActionError(action)
4648

4749
return state
4850

tests/test_features.py

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -68,50 +68,62 @@ def straight_reducer(
6868
action: ActionType,
6969
) -> CountStateType:
7070
if state is None:
71-
if isinstance(action, InitAction):
72-
return CountStateType(count=0)
73-
raise InitializationActionError(action)
74-
if isinstance(action, IncrementAction):
75-
return CountStateType(count=state.count + 1)
76-
if isinstance(action, DecrementByTwoAction):
77-
return CountStateType(count=state.count - 2)
78-
return state
71+
match action:
72+
case InitAction():
73+
return CountStateType(count=0)
74+
case _:
75+
raise InitializationActionError(action)
76+
match action:
77+
case IncrementAction():
78+
return CountStateType(count=state.count + 1)
79+
case DecrementByTwoAction():
80+
return CountStateType(count=state.count - 2)
81+
case _:
82+
return state
7983

8084

8185
def base10_reducer(
8286
state: CountStateType | None,
8387
action: ActionType,
8488
) -> CountStateType:
8589
if state is None:
86-
if isinstance(action, InitAction):
87-
return CountStateType(count=10)
88-
raise InitializationActionError(action)
89-
if isinstance(action, IncrementAction):
90-
return CountStateType(count=state.count + 1)
91-
if isinstance(action, DecrementByTwoAction):
92-
return CountStateType(count=state.count - 2)
93-
return state
90+
match action:
91+
case InitAction():
92+
return CountStateType(count=10)
93+
case _:
94+
raise InitializationActionError(action)
95+
match action:
96+
case IncrementAction():
97+
return CountStateType(count=state.count + 1)
98+
case DecrementByTwoAction():
99+
return CountStateType(count=state.count - 2)
100+
case _:
101+
return state
94102

95103

96104
def inverse_reducer(
97105
state: CountStateType | None,
98106
action: ActionType,
99107
) -> ReducerResult[CountStateType, IncrementAction, SleepEvent]:
100108
if state is None:
101-
if isinstance(action, InitAction):
102-
return CountStateType(count=0)
103-
raise InitializationActionError(action)
104-
if isinstance(action, IncrementAction):
105-
return CountStateType(count=state.count - 1)
106-
if isinstance(action, DecrementByTwoAction):
107-
return CountStateType(count=state.count + 2)
108-
if isinstance(action, DoNothingAction):
109-
return CompleteReducerResult(
110-
state=state,
111-
actions=[IncrementAction()],
112-
events=[SleepEvent(duration=0.1)],
113-
)
114-
return state
109+
match action:
110+
case InitAction():
111+
return CountStateType(count=0)
112+
case _:
113+
raise InitializationActionError(action)
114+
match action:
115+
case IncrementAction():
116+
return CountStateType(count=state.count - 1)
117+
case DecrementByTwoAction():
118+
return CountStateType(count=state.count + 2)
119+
case DoNothingAction():
120+
return CompleteReducerResult(
121+
state=state,
122+
actions=[IncrementAction()],
123+
events=[SleepEvent(duration=0.1)],
124+
)
125+
case _:
126+
return state
115127

116128

117129
Reducer: TypeAlias = tuple[

0 commit comments

Comments
 (0)