from abots.helpers import generator, infinitedict from gevent.lock import BoundedSemaphore from collections import namedtuple class AtomicDict(object): def __init__(self, *args, **kwargs): self._dict = dict() self._lock = BoundedSemaphore() def __enter__(self): self._lock.acquire() return self._dict def __exit__(self, _type, value, traceback): self._lock.release() def get_state(meta): state = infinitedict() with meta as m: state = m.get("state") assert state is not None, "'state' is missing" return state.copy() def unsubscribe(meta, listener): with meta as m: listeners = m.get("listeners") assert listener in listeners, f"'{listener}' missing from: {listeners}" listeners.remove(listener) def subscribe(meta, listener): with meta as m: listeners = m.get("listeners") assert listeners is not None, "'listeners' is missing" listeners.append(listener) return lambda: unsubscribe(meta, listener) @generator def dispatch(meta): action = (yield) with meta as m: is_dispatching = m.get("is_dispatching") assert is_dispatching is not None, "'is_dispatching' is missing" if is_dispatching: raise Exception("Reducers cannot dispatch actions") try: with meta as m: m["is_dispatching"] = True reducer = m.get("reducer") assert reducer is not None, "'reducer' is missing" m["state"] = reducer(state, action) finally: with meta as m: m["is_dispatching"] = False with meta as m: listeners = m.get("listeners") assert listeners is not None, "'listeners' is missing" for listener in listeners[:]: listener() def manager(reducer, initial_state=infinitedict()): meta = AtomicDict() with meta as m: m["listeners"] = list() m["is_dispatching"] = False m["reducer"] = reducer m["state"] = initial_state state = lambda: get_state(meta) return state, dispatch(meta) Action = namedtuple("Action", ["type", "payload"]) #meta, actions = manager(reducer)