Skip to content

Commit

Permalink
refactor(autorun): remove auto_call option as it was addressing suc…
Browse files Browse the repository at this point in the history
…h a rare use case that it was not worth the complexity
  • Loading branch information
sassanh committed Oct 2, 2024
1 parent 3708af6 commit 2942791
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 29 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "python-redux"
version = "0.16.1"
version = "0.17.0"
description = "Redux implementation for Python"
authors = ["Sassan Haradji <[email protected]>"]
license = "Apache-2.0"
Expand Down
7 changes: 1 addition & 6 deletions redux/autorun.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion redux/basic_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion redux/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
25 changes: 5 additions & 20 deletions tests/test_autorun.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
[
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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),
Expand All @@ -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,
),
Expand Down
80 changes: 80 additions & 0 deletions tests/test_combinations.py
Original file line number Diff line number Diff line change
@@ -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())
19 changes: 19 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 2942791

Please sign in to comment.