Skip to main content

Redux implementation for Python (Binary Extension)

Project description

🎛️ Python Redux

codecov PyPI - Python Version PyPI PyPI - License GitHub Workflow Status

🌟 Overview

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

🔎 Sample Usage

Minimal todo application store implemented using python-redux:

import uuid
from dataclasses import replace
from typing import Sequence

from immutable import Immutable

from redux import (
    BaseAction,
    BaseEvent,
    CompleteReducerResult,
    FinishAction,
    ReducerResult,
)
from redux.main import Store


# state:
class ToDoItem(Immutable):
    id: str
    content: str
    is_done: bool = False


class ToDoState(Immutable):
    items: Sequence[ToDoItem]


# actions:
class AddTodoItemAction(BaseAction):
    content: str


class MarkTodoItemDone(BaseAction):
    id: str


class RemoveTodoItemAction(BaseAction):
    id: str


# events:
class CallApi(BaseEvent):
    parameters: object


# reducer:
def reducer(
    state: ToDoState | None,
    action: BaseAction,
) -> ReducerResult[ToDoState, BaseAction, BaseEvent]:
    if state is None:
        return ToDoState(
            items=[
                ToDoItem(
                    id=uuid.uuid4().hex,
                    content='Initial Item',
                ),
            ],
        )
    if isinstance(action, AddTodoItemAction):
        return replace(
            state,
            items=[
                *state.items,
                ToDoItem(
                    id=uuid.uuid4().hex,
                    content=action.content,
                ),
            ],
        )
    if isinstance(action, RemoveTodoItemAction):
        return replace(
            state,
            actions=[item for item in state.items if item.id != action.id],
        )
    if isinstance(action, MarkTodoItemDone):
        return CompleteReducerResult(
            state=replace(
                state,
                items=[
                    replace(item, is_done=True) if item.id == action.id else item
                    for item in state.items
                ],
            ),
            events=[CallApi(parameters={})],
        )
    return state


store = Store(reducer)


# subscription:
dummy_render = print
store.subscribe(dummy_render)


# autorun:
@store.autorun(
    lambda state: state.items[0].content if len(state.items) > 0 else None,
)
def reaction(content: str | None) -> None:
    print(content)


@store.view(lambda state: state.items[0])
def first_item(first_item: ToDoItem) -> ToDoItem:
    return first_item


@store.view(lambda state: [item for item in state.items if item.is_done])
def done_items(done_items: list[ToDoItem]) -> list[ToDoItem]:
    return done_items


# event listener, note that this will run async in a separate thread, so it can include
# io operations like network calls, etc:
dummy_api_call = print
store.subscribe_event(
    CallApi,
    lambda event: dummy_api_call(event.parameters, done_items()),
)

# dispatch:
store.dispatch(AddTodoItemAction(content='New Item'))

store.dispatch(MarkTodoItemDone(id=first_item().id))

store.dispatch(FinishAction())

⚙️ Features

  • Redux API for Python developers.

  • Reduce boilerplate by dropping type property, payload classes and action creators:

    • Each action is a subclass of BaseAction.
    • Its type is checked by utilizing isinstance (no need for type property).
    • Its payload are its direct properties (no need for a separate payload object).
    • Its creator is its auto-generated constructor.
  • Use type annotations for all its API.

  • Immutable state management for predictable state updates using python-immutable.

  • Offers a streamlined, native API for handling side-effects asynchronously, eliminating the necessity for more intricate utilities such as redux-thunk or redux-saga.

  • Incorporates the autorun decorator and the view decorator, inspired by the mobx framework, to better integrate with elements of the software following procedural patterns.

  • Supports middlewares.

📦 Installation

The package handle in PyPI is python-redux

Pip

pip install python-redux

Poetry

poetry add python-redux

🛠 Usage

Handling Side Effects with Events

Python-redux introduces a powerful concept for managing side effects: Events. This approach allows reducers to remain pure while still signaling the need for side effects.

Why Events?

  • Separation of Concerns: By returning events, reducers stay pure and focused solely on state changes, delegating side effects to other parts of the software.
  • Flexibility: Events allow asynchronous operations like API calls to be handled separately, enhancing scalability and maintainability.

How to Use Events

  • Reducers: Reducers primarily return a new state. They can optionally return actions and events, maintaining their purity as these do not enact side effects themselves.
  • Dispatch Function: Besides actions, dispatch function can now accept events, enabling a more integrated flow of state and side effects.
  • Event Listeners: Implement listeners for specific events. These listeners handle the side effects (e.g., API calls) asynchronously.

Best Practices

  • Define Clear Events: Create well-defined events that represent specific side effects.
  • Use Asynchronously: Design event listeners to operate asynchronously, keeping your application responsive. Note that python-redux, by default, runs all event handler functions in new threads.

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 todo demo or features demo to see it in action.

Autorun Decorator

Inspired by MobX's autorun and reaction, python-redux introduces the autorun decorator. This decorator requires a selector function as an argument. The selector is a function that accepts the store instance and returns a derived object from the store's state. The primary function of autorun is to establish a subscription to the store. Whenever the store is changed, autorun executes the selector with the updated store. Importantly, the decorated function is triggered only if there is a change in the 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 todo demo or features demo to see it in action.

View Decorator

Inspired by MobX's computed, python-redux introduces the view decorator. It takes a selector and each time the decorated function is called, it only runs the function body if the returned value of the selector is changed, otherwise it simply returns the previous value. So unlike computed of MobX, it doesn't extract the requirements of the function itself, you need to provide them in the return value of the selector function.

Combining reducers - combine_reducers

You can compose high level reducers by combining smaller reducers using combine_reducers utility function. This works mostly the same as the JS redux library version except that it provides a mechanism to dynamically add/remove reducers to/from it. This is done by generating an id and returning it along the generated reducer. This id is used to refer to this reducer in the future. Let's assume you composed a reducer like this:

reducer, reducer_id = combine_reducers(
    state_type=StateType,
    first=straight_reducer,
    second=second_reducer,
)

You can then add a new reducer to it using the reducer_id like this:

store.dispatch(
    CombineReducerRegisterAction(
        combine_reducers_id=reducer_id,
        key='third',
        third=third_reducer,
    ),
)

You can also remove a reducer from it like this:

store.dispatch(
    CombineReducerRegisterAction(
        combine_reducers_id=reducer_id,
        key='second',
    ),
)

Without this id, all the combined reducers in the store tree would register third reducer and unregister second reducer, but thanks to this reducer_id, these actions will only target the desired combined reducer.

🎉 Demo

For a detailed example, see features demo.

🤝 Contributing

Contributions following Python best practices are welcome.

📜 License

This project is released under the Apache-2.0 License. See the LICENSE file for more details.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

python_redux-0.25.2.dev326020910310098575-cp312-cp312-macosx_10_13_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.12macOS 10.13+ x86-64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_arm64.whl (977.5 kB view details)

Uploaded CPython 3.11Windows ARM64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_amd64.whl (1.0 MB view details)

Uploaded CPython 3.11Windows x86-64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-win32.whl (989.4 kB view details)

Uploaded CPython 3.11Windows x86

python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_aarch64.whl (2.8 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ ARM64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (2.8 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_10_9_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11macOS 10.9+ x86-64

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp312-cp312-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp312-cp312-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 3b2be5a8186fe85ab2a09743ce431e204778f2ad13fabb506f0e0c547a0c3dd2
MD5 d73944dd6cff91f9797bf075a4251980
BLAKE2b-256 602c4e7f6bfd6eed14137e4bed5694df6641a0bba07ad267a77c53bd0f18be14

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp312-cp312-macosx_10_13_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_arm64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_arm64.whl
Algorithm Hash digest
SHA256 760ad4792be1d6e45912f70d2aa571d35682f2e15caa7c515476c2b9c7b511cc
MD5 07e083383ea2625b243e1777e454e39f
BLAKE2b-256 5f0160ed0af00eafdfe82588b8c51bcdced454624e98fdbf6d9177a69dc9dd31

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_arm64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 5f6494c20a160dd9c6df879eb2b1635443679200c17e200dad2eb716a3908f7e
MD5 608ddd379e369001ff2ff637b5d35940
BLAKE2b-256 038f51dec1ed007740eca6340a9742b88028cc2ac15728903527ad3c5b530e0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-win_amd64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-win32.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-win32.whl
Algorithm Hash digest
SHA256 6fc716e2fe6b09aa93eb3776a84780f62867df3453a1e1a0acb7da0574db2e2b
MD5 6354f3987226023f2f8e9daf93cd4f95
BLAKE2b-256 c899dba942f091c2680ced733d4380cd8da184c4558e99ee176340880117833b

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-win32.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 7657ca41b4f40be3ec8de7aac72b815e3cf9db62cf655e40938716f1e5636ff5
MD5 d97fe21a392abdc33c2a7effbde7f91e
BLAKE2b-256 0300f99d8380091d977d7d6622c2bd750dae17df61128c7627d00e3a2df87352

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 e707e37860a660ae6e4eafe7254a8bc3f9c64a7a1d87db4a93813a2aead268ce
MD5 d18c728ace1d5053872cc4af70548b44
BLAKE2b-256 e55a59922807a5efa3fe7b127090dc3ecca94d26424aae4229347906cfca5574

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-musllinux_1_2_aarch64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e9f4f298ea8c4ed6d57fc3d0e28c07c56afaaada8e1d0ddeb5b46c0343ba4d6d
MD5 217836b798f08edc02a5f7b36281681d
BLAKE2b-256 0fece9ffdd7359220e583b9ec5f831fd8bdd55c83abd5c587d3bcf07911bf1a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 1da5b03e9681e79fc573ea91f0df59176f38f424d07f13133693b80d903c3338
MD5 a3cc4d664181f460a387475db2151fac
BLAKE2b-256 4656b84593c88153fd8b02f653db1288c18db20f6dc09bd48cf833668dc636a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 211c8a79311deb6653ec08b16d668b16f0674788a49fc1c165f114fddb951ee6
MD5 533d1052d328d18f57444dbb93bc6de9
BLAKE2b-256 546a70bd947f2a50f4e18dedf65c9f86ff84a914f93d6dbcab594e90c6ba3d37

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 f9affdc36001495197ffde7e1055c9da3440aae0aaa9fe3cc661577baefe5f25
MD5 963b75c7085d20e2d156fa661d595023
BLAKE2b-256 733ec5990ec458d1e9fa316751c7851a9e043a71334040d611af346a9bde319e

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev326020910310098575-cp311-cp311-macosx_10_9_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page