diff --git a/CHANGELOG.md b/CHANGELOG.md index 446ab2b..0672b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Version 0.12.2 + +- docs: update path of demos migrated to tests in `README.md` +- refactor: remove `set_customer_serializer` in favor of overridable `serialize_value` + ## Version 0.12.1 - refactor: move store serializer from test framework to code `Store` class diff --git a/README.md b/README.md index 0f2391a..450ef75 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,8 @@ side effects. This concept fills the gap in handling side effects within Redux's ecosystem, offering a more nuanced and integrated approach to state and side effect management. -See todo sample below or check the [demo](demo.py) to see it in action. +See todo sample below or check the [todo demo](/tests/test_todo.py) or +[features demo](/tests/test_features.py) to see it in action. ### Autorun Decorator @@ -97,7 +98,8 @@ selector's return value. This mechanism ensures that the decorated function runs in response to relevant state changes, enhancing efficiency and responsiveness in the application. -See todo sample below or check the [demo](demo.py) to see it in action. +See todo sample below or check the [todo demo](/tests/test_todo.py) or +[features demo](/tests/test_features.py) to see it in action. ### Combining reducers - `combine_reducers` @@ -241,7 +243,7 @@ store.dispatch(FinishAction()) ## 🎉 Demo -For a detailed example, see `demo.py` in the [GitHub repository](https://github.com/sassanh/python-redux). +For a detailed example, see [features demo](/tests/test_features.py). ## 🤝 Contributing diff --git a/pyproject.toml b/pyproject.toml index 9afd3f5..cb54bfe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-redux" -version = "0.12.1" +version = "0.12.2" description = "Redux implementation for Python" authors = ["Sassan Haradji "] license = "Apache-2.0" diff --git a/redux/main.py b/redux/main.py index 3c26cb9..c0b48fe 100644 --- a/redux/main.py +++ b/redux/main.py @@ -1,4 +1,4 @@ -# ruff: noqa: D100, D101, D102, D103, D104, D105, D107 +"""Redux store for managing state and side effects.""" from __future__ import annotations import dataclasses @@ -74,13 +74,14 @@ def run(self: _SideEffectRunnerThread[Event]) -> None: class Store(Generic[State, Action, Event]): - custom_serializer = None + """Redux store for managing state and side effects.""" def __init__( self: Store[State, Action, Event], reducer: ReducerType[State, Action, Event], options: CreateStoreOptions | None = None, ) -> None: + """Create a new store.""" self.store_options = options or CreateStoreOptions() self.reducer = reducer self._create_task: Callable[[Coroutine], Any] = ( @@ -175,6 +176,7 @@ def _run_event_handlers(self: Store[State, Action, Event]) -> None: cast(Callable[[], Any], event_handler)() def run(self: Store[State, Action, Event]) -> None: + """Run the store.""" with self._is_running: while len(self._actions) > 0 or len(self._events) > 0: if len(self._actions) > 0: @@ -189,6 +191,7 @@ def dispatch( with_state: Callable[[State | None], DispatchParameters[Action, Event]] | None = None, ) -> None: + """Dispatch actions and/or events.""" if with_state is not None: self.dispatch(with_state(self._state)) @@ -217,6 +220,7 @@ def subscribe( *, keep_ref: bool = True, ) -> Callable[[], None]: + """Subscribe to state changes.""" if keep_ref: listener_ref = listener elif inspect.ismethod(listener): @@ -234,6 +238,7 @@ def subscribe_event( *, options: EventSubscriptionOptions | None = None, ) -> Callable[[], None]: + """Subscribe to events.""" subscription_options = ( EventSubscriptionOptions() if options is None else options ) @@ -271,6 +276,8 @@ def autorun( SelectorOutput, AutorunOriginalReturnType, ]: + """Create a new autorun, reflecting on state changes.""" + def decorator( func: Callable[[SelectorOutput], AutorunOriginalReturnType] | Callable[[SelectorOutput, SelectorOutput], AutorunOriginalReturnType], @@ -285,26 +292,19 @@ def decorator( return decorator - def set_custom_serializer( - self: Store, - serializer: Callable[[object | type], SnapshotAtom], - ) -> None: - """Set a custom serializer for the store snapshot.""" - self.custom_serializer = serializer - @property def snapshot(self: Store[State, Action, Event]) -> SnapshotAtom: - return self._serialize_value(self._state) + """Return a snapshot of the current state of the store.""" + return self.serialize_value(self._state) - def _serialize_value(self: Store, obj: object | type) -> SnapshotAtom: - if self.custom_serializer: - return self.custom_serializer(obj) + def serialize_value(self: Store, obj: object | type) -> SnapshotAtom: + """Serialize a value to a snapshot atom.""" if is_immutable(obj): return self._serialize_dataclass_to_dict(obj) if isinstance(obj, (list, tuple)): - return [self._serialize_value(i) for i in obj] + return [self.serialize_value(i) for i in obj] if callable(obj): - return self._serialize_value(obj()) + return self.serialize_value(obj()) if isinstance(obj, StrEnum): return str(obj) if isinstance(obj, IntEnum): @@ -320,6 +320,6 @@ def _serialize_dataclass_to_dict( ) -> dict[str, Any]: result = {} for field in dataclasses.fields(obj): - value = self._serialize_value(getattr(obj, field.name)) + value = self.serialize_value(getattr(obj, field.name)) result[field.name] = value return result