Skip to content

Commit 1050f21

Browse files
committed
docs: add an introduction of view to README.md
1 parent 90d803d commit 1050f21

File tree

3 files changed

+154
-109
lines changed

3 files changed

+154
-109
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.15.3
4+
5+
- docs: add an introduction of `view` to `README.md`
6+
37
## Version 0.15.2
48

59
- refactor(autorun): improve type-hints so that its final return value has the correct

README.md

Lines changed: 149 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,164 @@
11
# 🎛️ Python Redux
22

3-
[![image](https://img.shields.io/pypi/v/python-redux.svg)](https://pypi.python.org/pypi/python-redux)
4-
[![image](https://img.shields.io/pypi/l/python-redux.svg)](https://github.com/sassanh/python-redux/LICENSE)
5-
[![image](https://img.shields.io/pypi/pyversions/python-redux.svg)](https://pypi.python.org/pypi/python-redux)
6-
[![Actions status](https://github.com/sassanh/python-redux/workflows/CI/CD/badge.svg)](https://github.com/sassanh/python-redux/actions)
7-
[![codecov](https://codecov.io/gh/sassanh/python-redux/graph/badge.svg?token=4F3EWZRLCL)](https://codecov.io/gh/sassanh/python-redux)
8-
93
## 🌟 Overview
104

115
Python Redux is a Redux implementation for Python, bringing Redux's state management
126
architecture to Python applications.
137

8+
### 🔎 Sample Usage
9+
10+
Minimal todo application store implemented using python-redux:
11+
12+
```python
13+
import uuid
14+
from dataclasses import replace
15+
from typing import Sequence
16+
17+
from immutable import Immutable
18+
19+
from redux import (
20+
BaseAction,
21+
BaseEvent,
22+
CompleteReducerResult,
23+
FinishAction,
24+
ReducerResult,
25+
)
26+
from redux.main import Store
27+
28+
29+
# state:
30+
class ToDoItem(Immutable):
31+
id: str
32+
content: str
33+
is_done: bool = False
34+
35+
36+
class ToDoState(Immutable):
37+
items: Sequence[ToDoItem]
38+
39+
40+
# actions:
41+
class AddTodoItemAction(BaseAction):
42+
content: str
43+
44+
45+
class MarkTodoItemDone(BaseAction):
46+
id: str
47+
48+
49+
class RemoveTodoItemAction(BaseAction):
50+
id: str
51+
52+
53+
# events:
54+
class CallApi(BaseEvent):
55+
parameters: object
56+
57+
58+
# reducer:
59+
def reducer(
60+
state: ToDoState | None,
61+
action: BaseAction,
62+
) -> ReducerResult[ToDoState, BaseAction, BaseEvent]:
63+
if state is None:
64+
return ToDoState(
65+
items=[
66+
ToDoItem(
67+
id=uuid.uuid4().hex,
68+
content='Initial Item',
69+
),
70+
],
71+
)
72+
if isinstance(action, AddTodoItemAction):
73+
return replace(
74+
state,
75+
items=[
76+
*state.items,
77+
ToDoItem(
78+
id=uuid.uuid4().hex,
79+
content=action.content,
80+
),
81+
],
82+
)
83+
if isinstance(action, RemoveTodoItemAction):
84+
return replace(
85+
state,
86+
actions=[item for item in state.items if item.id != action.id],
87+
)
88+
if isinstance(action, MarkTodoItemDone):
89+
return CompleteReducerResult(
90+
state=replace(
91+
state,
92+
items=[
93+
replace(item, is_done=True) if item.id == action.id else item
94+
for item in state.items
95+
],
96+
),
97+
events=[CallApi(parameters={})],
98+
)
99+
return state
100+
101+
102+
store = Store(reducer)
103+
104+
105+
# subscription:
106+
dummy_render = print
107+
store.subscribe(dummy_render)
108+
109+
110+
# autorun:
111+
@store.autorun(
112+
lambda state: state.items[0].content if len(state.items) > 0 else None,
113+
)
114+
def reaction(content: str | None) -> None:
115+
print(content)
116+
117+
118+
@store.view(lambda state: state.items[0])
119+
def first_item(first_item: ToDoItem) -> ToDoItem:
120+
return first_item
121+
122+
123+
@store.view(lambda state: [item for item in state.items if item.is_done])
124+
def done_items(done_items: list[ToDoItem]) -> list[ToDoItem]:
125+
return done_items
126+
127+
128+
# event listener, note that this will run async in a separate thread, so it can include
129+
# io operations like network calls, etc:
130+
dummy_api_call = print
131+
store.subscribe_event(
132+
CallApi,
133+
lambda event: dummy_api_call(event.parameters, done_items()),
134+
)
135+
136+
# dispatch:
137+
store.dispatch(AddTodoItemAction(content='New Item'))
138+
139+
store.dispatch(MarkTodoItemDone(id=first_item().id))
140+
141+
store.dispatch(FinishAction())
142+
```
143+
14144
## ⚙️ Features
15145

16146
- Redux API for Python developers.
17147
- Reduce boilerplate by dropping `type` property, payload classes and action creators:
18148

19149
- Each action is a subclass of `BaseAction`.
20150
- Its type is checked by utilizing `isinstance` (no need for `type` property).
21-
- Its payload are its direct properties (no need for `payload` property).
151+
- Its payload are its direct properties (no need for a separate `payload` object).
22152
- Its creator is its auto-generated constructor.
23153

24-
➡️ [Sample usage](#-usage)
25-
26154
- Use type annotations for all its API.
27155
- Immutable state management for predictable state updates using [python-immutable](https://github.com/sassanh/python-immutable).
28156
- Offers a streamlined, native [API](#handling-side-effects-with-events) for handling
29157
side-effects asynchronously, eliminating the necessity for more intricate utilities
30158
such as redux-thunk or redux-saga.
31-
- Incorporates the [autorun decorator](#autorun-decorator), inspired
32-
by the mobx framework, to better integrate with elements of the software following
33-
procedural patterns.
159+
- Incorporates the [autorun decorator](#autorun-decorator) and
160+
the [view decorator](#view-decorator), inspired by the mobx framework, to better
161+
integrate with elements of the software following procedural patterns.
34162
- Supports middlewares.
35163

36164
## 📦 Installation
@@ -105,6 +233,15 @@ the application.
105233
See todo sample below or check the [todo demo](/tests/test_todo.py) or
106234
[features demo](/tests/test_features.py) to see it in action.
107235

236+
### View Decorator
237+
238+
Inspired by MobX's [computed](https://mobx.js.org/computeds.html), python-redux introduces
239+
the view decorator. It takes a selector and each time the decorated function is called,
240+
it only runs the function body if the returned value of the selector is changed,
241+
otherwise it simply returns the previous value. So unlike `computed` of MobX, it
242+
doesn't extract the requirements of the function itself, you need to provide them
243+
in the return value of the selector function.
244+
108245
### Combining reducers - `combine_reducers`
109246

110247
You can compose high level reducers by combining smaller reducers using `combine_reducers`
@@ -149,102 +286,6 @@ Without this id, all the combined reducers in the store tree would register `thi
149286
reducer and unregister `second` reducer, but thanks to this `reducer_id`, these
150287
actions will only target the desired combined reducer.
151288

152-
### 🔎 Sample Usage
153-
154-
Minimal todo application store implemented using python-redux:
155-
156-
```python
157-
# state:
158-
class ToDoItem(Immutable):
159-
id: str
160-
content: str
161-
timestamp: float
162-
163-
164-
class ToDoState(Immutable):
165-
items: Sequence[ToDoItem]
166-
167-
168-
# actions:
169-
class AddTodoItemAction(BaseAction):
170-
content: str
171-
timestamp: float
172-
173-
174-
class RemoveTodoItemAction(BaseAction):
175-
id: str
176-
177-
178-
# events:
179-
class CallApi(BaseEvent):
180-
parameters: object
181-
182-
183-
# reducer:
184-
def reducer(
185-
state: ToDoState | None,
186-
action: BaseAction,
187-
) -> ReducerResult[ToDoState, BaseAction, BaseEvent]:
188-
if state is None:
189-
return ToDoState(
190-
items=[
191-
ToDoItem(
192-
id=uuid.uuid4().hex,
193-
content='Initial Item',
194-
timestamp=time.time(),
195-
),
196-
],
197-
)
198-
if isinstance(action, AddTodoItemAction):
199-
return replace(
200-
state,
201-
items=[
202-
*state.items,
203-
ToDoItem(
204-
id=uuid.uuid4().hex,
205-
content=action.content,
206-
timestamp=action.timestamp,
207-
),
208-
],
209-
)
210-
if isinstance(action, RemoveTodoItemAction):
211-
return CompleteReducerResult(
212-
state=replace(
213-
state,
214-
actions=[item for item in state.items if item.id != action.id],
215-
),
216-
events=[CallApi(parameters={})],
217-
)
218-
return state
219-
220-
221-
store = create_store(reducer)
222-
223-
224-
# subscription:
225-
dummy_render = print
226-
store.subscribe(dummy_render)
227-
228-
229-
# autorun:
230-
@store.autorun(
231-
lambda state: state.items[0].content if len(state.items) > 0 else None,
232-
)
233-
def reaction(content: str | None) -> None:
234-
print(content)
235-
236-
237-
# event listener, note that this will run async in a separate thread, so it can include
238-
# io operations like network calls, etc:
239-
dummy_api_call = print
240-
store.subscribe_event(CallApi, lambda event: dummy_api_call(event.parameters))
241-
242-
# dispatch:
243-
store.dispatch(AddTodoItemAction(content='New Item', timestamp=time.time()))
244-
245-
store.dispatch(FinishAction())
246-
```
247-
248289
## 🎉 Demo
249290

250291
For a detailed example, see [features demo](/tests/test_features.py).

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.15.2"
3+
version = "0.15.3"
44
description = "Redux implementation for Python"
55
authors = ["Sassan Haradji <[email protected]>"]
66
license = "Apache-2.0"

0 commit comments

Comments
 (0)