diff --git a/CHANGELOG.md b/CHANGELOG.md index 4073c37..11eb7a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Version 0.17.0 + +- refactor(autorun): remove `auto_call` option as it was addressing such a rare use case that it was not worth the complexity + ## Version 0.16.1 - feat(core): add `_type` field to the serialized immutable instances diff --git a/redux/autorun.py b/redux/autorun.py index 82d4ce6..2fcfbc6 100644 --- a/redux/autorun.py +++ b/redux/autorun.py @@ -45,9 +45,6 @@ def __init__( ], options: AutorunOptions[AutorunOriginalReturnType], ) -> None: - if not options.reactive and options.auto_call: - msg = '`reactive` must be `True` if `auto_call` is `True`' - raise ValueError(msg) self._store = store self._selector = selector self._comparator = comparator @@ -80,9 +77,7 @@ def __init__( if self._options.reactive: self._unsubscribe = store.subscribe( - lambda state: self._call() - if self._check(state) and self._options.auto_call - else None, + lambda state: self._call() if self._check(state) else None, ) else: self._unsubscribe = None diff --git a/redux/basic_types.py b/redux/basic_types.py index c08f1a2..01a280e 100644 --- a/redux/basic_types.py +++ b/redux/basic_types.py @@ -134,7 +134,6 @@ class CreateStoreOptions(Immutable, Generic[Action, Event]): class AutorunOptions(Immutable, Generic[AutorunOriginalReturnType]): default_value: AutorunOriginalReturnType | None = None initial_call: bool = True - auto_call: bool = True reactive: bool = True keep_ref: bool = True subscribers_initial_run: bool = True diff --git a/redux/main.py b/redux/main.py index 319e3ea..ecb3c6c 100644 --- a/redux/main.py +++ b/redux/main.py @@ -389,7 +389,6 @@ def decorator( options=AutorunOptions( default_value=_options.default_value, initial_call=False, - auto_call=False, reactive=False, keep_ref=_options.keep_ref, subscribers_initial_run=_options.subscribers_initial_run, diff --git a/tests/test_autorun.py b/tests/test_autorun.py index 339f0e2..07da8c5 100644 --- a/tests/test_autorun.py +++ b/tests/test_autorun.py @@ -183,20 +183,6 @@ def render(value: int) -> int: ) -def test_auto_call_without_reactive(store: StoreType) -> None: - with pytest.raises( - ValueError, - match='^`reactive` must be `True` if `auto_call` is `True`$', - ): - - @store.autorun( - lambda state: state.value, - options=AutorunOptions(reactive=False, auto_call=True), - ) - def _(_: int) -> int: - pytest.fail('This should never be called') - - call_sequence = [ # 0 [ @@ -229,7 +215,7 @@ def render(_: int) -> None: ... render_autorun = store.autorun( lambda state: state.value, - options=AutorunOptions(reactive=True, auto_call=False, initial_call=True), + options=AutorunOptions(reactive=False, initial_call=True), )(render) for actions in call_sequence: @@ -250,7 +236,7 @@ def render(_: int) -> None: ... render_autorun = store.autorun( lambda state: state.value, - options=AutorunOptions(reactive=True, auto_call=False, initial_call=False), + options=AutorunOptions(reactive=False, initial_call=False), )(render) for actions in call_sequence: @@ -269,15 +255,15 @@ def render(_: int) -> None: ... render = mocker.create_autospec(render) - render_autorun = store.autorun( + trigger_autorun = store.autorun( lambda state: state.value, - options=AutorunOptions(reactive=True, auto_call=True, initial_call=True), + options=AutorunOptions(reactive=True, initial_call=True), )(render) for actions in call_sequence: for action in actions: store.dispatch(action) - render_autorun() + trigger_autorun() assert render.mock_calls == [ call(0), @@ -299,7 +285,6 @@ def test_view_mode_with_arguments_autorun( lambda state: state.value, options=AutorunOptions( reactive=False, - auto_call=False, initial_call=False, default_value=0, ), diff --git a/tests/test_combinations.py b/tests/test_combinations.py new file mode 100644 index 0000000..bce19de --- /dev/null +++ b/tests/test_combinations.py @@ -0,0 +1,80 @@ +# ruff: noqa: D100, D101, D102, D103, D104, D107 +from __future__ import annotations + +from dataclasses import replace +from typing import Literal + +import pytest +from immutable import Immutable + +from redux.basic_types import ( + BaseAction, + CompleteReducerResult, + CreateStoreOptions, + FinishAction, + FinishEvent, + InitAction, + InitializationActionError, +) +from redux.main import Store + + +class StateType(Immutable): + value1: int + value2: int + + +class IncrementAction(BaseAction): + which: Literal[1, 2] + + +Action = IncrementAction | InitAction | FinishAction + + +def reducer( + state: StateType | None, + action: Action, +) -> StateType | CompleteReducerResult[StateType, Action, FinishEvent]: + if state is None: + if isinstance(action, InitAction): + return StateType(value1=0, value2=0) + raise InitializationActionError(action) + + if isinstance(action, IncrementAction): + field_name = f'value{action.which}' + return replace( + state, + **{field_name: getattr(state, field_name) + 1}, + ) + + return state + + +StoreType = Store[StateType, Action, FinishEvent] + + +@pytest.fixture +def store() -> StoreType: + return Store(reducer, options=CreateStoreOptions(auto_init=True)) + + +def test_autorun_of_view(store: StoreType) -> None: + @store.autorun( + lambda state: state.value2, + lambda state: (state.value1, state.value2), + ) + @store.view(lambda state: state.value1) + def view(value1: int, value2: int) -> tuple[int, int]: + return (value1, value2) + + assert view() == (0, 0) + + store.dispatch(IncrementAction(which=1)) + + assert view() == (1, 0) + + store.dispatch(IncrementAction(which=2)) + + assert view() == (1, 1) + + store.dispatch(FinishAction()) diff --git a/tests/test_views.py b/tests/test_views.py index f84c7c8..4082519 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -85,6 +85,25 @@ def render(value: int) -> int: assert render() == 1 +def test_not_reactive( + store: StoreType, +) -> None: + runs = 0 + + @store.view(lambda state: state.value) + def render(value: int) -> int: + nonlocal runs + runs += 1 + return value + + store.dispatch(IncrementAction()) + store.dispatch(IncrementAction()) + store.dispatch(IncrementAction()) + + assert render() == 3 + assert runs == 1 + + def test_uninitialized_store( store: StoreType, ) -> None: