Skip to content

Instantly share code, notes, and snippets.

@nakamuray
Created October 30, 2013 12:56
Show Gist options
  • Save nakamuray/7232229 to your computer and use it in GitHub Desktop.
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)
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