Created
October 30, 2013 12:56
-
-
Save nakamuray/7232229 to your computer and use it in GitHub Desktop.
state monad like feature for python, with deferred support (it's not monad at all, though)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import print_function | |
import functools | |
import types | |
from twisted.internet import defer | |
__all__ = ['StatefulCallbacks', 'statefulCallbacks', 'returnValue'] | |
class StatefulCallbacks(object): | |
def __init__(self, gen, args, kwargs): | |
self._gen = gen | |
self._args = args | |
self._kwargs = kwargs | |
def eval(self, state): | |
return self.run(state).addCallback(lambda (v, s): v) | |
@defer.inlineCallbacks | |
def run(self, state): | |
g = self._gen(*self._args, **self._kwargs) | |
if not isinstance(g, types.GeneratorType): | |
raise TypeError( | |
'{0} returns non generator object {1}'.format(self._gen, g)) | |
result = next(g) | |
while True: | |
if isinstance(result, StatefulCallbacks): | |
(next_input, state) = yield result.run(state) | |
elif isinstance(result, _GetState): | |
next_input = state | |
elif isinstance(result, _SetState): | |
next_input = None | |
state = result.state | |
elif isinstance(result, defer.Deferred): | |
next_input = yield result | |
else: | |
next_input = result | |
try: | |
result = g.send(next_input) | |
except StopIteration: | |
defer.returnValue((None, state)) | |
except defer._DefGen_Return as e: | |
defer.returnValue((e.value, state)) | |
def statefulCallbacks(func): | |
@functools.wraps(func) | |
def wrapper(*args, **kwargs): | |
return StatefulCallbacks(func, args, kwargs) | |
return wrapper | |
class _GetState(object): | |
pass | |
get_state = _GetState | |
class _SetState(object): | |
def __init__(self, state): | |
self.state = state | |
set_state = _SetState | |
returnValue = defer.returnValue | |
@defer.inlineCallbacks | |
def test(reactor): | |
def sleep(sec): | |
d = defer.Deferred() | |
reactor.callLater(sec, d.callback, None) | |
return d | |
@statefulCallbacks | |
def sleep_and_print_state(sec): | |
print('sleeping ...') | |
yield sleep(sec) | |
st = yield get_state() | |
print(st) | |
yield sleep_and_print_state(1).eval("I'm state!") | |
class MyState(object): | |
def __init__(self, name, age): | |
self.name = name | |
self.age = age | |
@statefulCallbacks | |
def state_reader(attr_name): | |
st = yield get_state() | |
yield sleep(0.1) | |
defer.returnValue(getattr(st, attr_name)) | |
ret = yield state_reader('name').eval(MyState('my name', None)) | |
assert ret == 'my name' | |
@statefulCallbacks | |
def get_name_and_age(): | |
name = yield state_reader('name') | |
age = yield state_reader('age') | |
defer.returnValue((name, age)) | |
ret = yield get_name_and_age().eval(MyState('my name', 20)) | |
assert ret == ('my name', 20) | |
if __name__ == '__main__': | |
from twisted.internet.task import react | |
react(test, ()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment