Tomi Cvetic пре 7 година
комит
9b919f0206
7 измењених фајлова са 248 додато и 0 уклоњено
  1. 14 0
      .gitignore
  2. 0 0
      __init__.py
  3. 84 0
      calculate.py
  4. 55 0
      counter.py
  5. 75 0
      project.py
  6. 19 0
      pydux_thunk.py
  7. 1 0
      requirements.txt

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+# Virtualenv
+# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
+.Python
+[Bb]in
+[Ii]nclude
+[Ll]ib
+[Ll]ib64
+[Ll]ocal
+[Ss]cripts
+[Ss]hare
+__pycache__
+pyvenv.cfg
+.venv
+pip-selfcheck.json


+ 84 - 0
calculate.py

@@ -0,0 +1,84 @@
+import pydux_thunk
+import asyncio
+
+initial_state = {
+	'running': False,
+	'result': None,
+	'error': None
+}
+
+class ActionTypes(object):
+	CALCULATE = 'calculate/CALCULATE'
+	START = 'calculate/START'
+	FAILURE = 'calculate/FAILURE'
+	SUCCESS = 'calculate/SUCCESS'
+
+class ActionCreators(object):
+	def calculate(value1, value2):
+		return thunk(value1, value2)
+
+	def start():
+		return dict(
+			type=ActionTypes.START
+		)
+
+	def success(result):
+		return dict(
+			type=ActionTypes.SUCCESS,
+			result=result
+		)
+
+	def failure(error):
+		return dict(
+			type=ActionTypes.FAILURE,
+			error=error
+		)
+
+def reducer (state=None, action=None):
+	if state is None:
+		state = initial_state
+	if not (isinstance(action, dict) and 'type' in action):
+		return state
+	if action['type'] == ActionTypes.CALCULATE:
+		state['running'] = True
+	elif action['type'] == ActionTypes.SUCCESS:
+		state['running'] = False
+		state['result'] = action['result']
+		state['error'] = None
+	elif action['type'] == ActionTypes.FAILURE:
+		state['running'] = False
+		state['result'] = None
+		state['error'] = action['error']
+	return state
+
+async def calculate(value1, value2):
+	print('calculating pi', value1, value2)
+	await asyncio.sleep(5)
+	return data**2
+
+def thunk(value1, value2):
+	async def wrapper(dispatch, get_state):
+		dispatch(ActionCreators.calculate())
+		result = await calculate(value1, value2)
+		dispatch(ActionCreators.success(result))
+	return wrapper
+
+if __name__ == '__main__':
+	import unittest
+
+	class TestSuite(unittest.TestCase):
+		def test_action_creators(self):
+			#self.assertEqual(ActionCreators.calculate(1, 2), thunk(1, 2))
+			self.assertEqual(ActionCreators.start(), {'type': 'calculate/START'})
+			self.assertEqual(ActionCreators.success(4), {'type': 'calculate/SUCCESS', 'result': 4})
+			#self.assertEqual(ActionCreators.failure(ZeroDivisionError()), {'type': 'calculate/FAILURE', 'error': ZeroDivisionError()})
+
+		def test_reducer(self):
+			self.assertEqual(reducer(None, ActionCreators.calculate(6,2)), {'running': False, 'result': None, 'error': None})
+			#self.assertEqual(reducer(452, ActionCreators.set_state(13)), 13)
+			#self.assertEqual(reducer(None, ActionCreators.increment()), 1)
+			#self.assertEqual(reducer(341, ActionCreators.increment()), 342)
+			#self.assertEqual(reducer(None, ActionCreators.decrement()), -1)
+			#self.assertEqual(reducer(251, ActionCreators.decrement()), 250)
+
+	unittest.main()

+ 55 - 0
counter.py

@@ -0,0 +1,55 @@
+initial_state = 0
+
+class ActionTypes(object):
+	SET_STATE = 'counter/SET_STATE'
+	INCREMENT = 'counter/INCREMENT'
+	DECREMENT = 'counter/DECREMENT'
+
+class ActionCreators(object):
+	def set_state(value):
+		return dict(
+			type=ActionTypes.SET_STATE,
+			value=value
+		)
+
+	def increment():
+		return dict(
+			type=ActionTypes.INCREMENT
+		)
+
+	def decrement():
+		return dict(
+			type=ActionTypes.DECREMENT
+		)
+
+def reducer (state=None, action=None):
+	if state is None:
+		state = initial_state
+	if not (isinstance(action, dict) and 'type' in action):
+		return state
+	if action['type'] == ActionTypes.SET_STATE:
+		state = action['value'] 
+	elif action['type'] == ActionTypes.INCREMENT:
+		state = state + 1
+	elif action['type'] == ActionTypes.DECREMENT:
+		state = state - 1
+	return state
+
+if __name__ == '__main__':
+	import unittest
+
+	class TestSuite(unittest.TestCase):
+		def test_action_creators(self):
+			self.assertEqual(ActionCreators.set_state(12), {'type': 'counter/SET_STATE', 'value': 12})
+			self.assertEqual(ActionCreators.increment(), {'type': 'counter/INCREMENT'})
+			self.assertEqual(ActionCreators.decrement(), {'type': 'counter/DECREMENT'})
+
+		def test_reducer(self):
+			self.assertEqual(reducer(None, ActionCreators.set_state(13)), 13)
+			self.assertEqual(reducer(452, ActionCreators.set_state(13)), 13)
+			self.assertEqual(reducer(None, ActionCreators.increment()), 1)
+			self.assertEqual(reducer(341, ActionCreators.increment()), 342)
+			self.assertEqual(reducer(None, ActionCreators.decrement()), -1)
+			self.assertEqual(reducer(251, ActionCreators.decrement()), 250)
+
+	unittest.main()

+ 75 - 0
project.py

@@ -0,0 +1,75 @@
+import pydux
+import asyncio
+import time
+import threading
+import counter
+import calculate
+import pydux_thunk
+
+def loop_in_thread(loop):
+	async def main_task():
+		print('Starting loop')
+		while True:
+			await asyncio.sleep(1)
+			pending = asyncio.Task.all_tasks()
+			my_sum = 0
+			for task in pending:
+				if not task.done():
+					my_sum += 1
+			if my_sum == 1:
+				break
+		print('All tasks finished, exiting loop.')
+
+	asyncio.set_event_loop(loop)
+	loop.run_until_complete(main_task())
+	loop.close()
+
+loop = asyncio.get_event_loop()
+t = threading.Thread(target=loop_in_thread, args=(loop,))
+t.start()
+
+def log_middleware(store):
+	def wrapper(next_):
+		def log_dispatch(action):
+			print('Dispatch Action:', action)
+			return next_(action)
+		return log_dispatch
+	return wrapper
+
+
+
+initial_state = {
+	'counter': counter.initial_state,
+	'calculate': calculate.initial_state
+}
+
+root_reducer = pydux.combine_reducers({
+	'counter': counter.reducer,
+	'calculate': calculate.reducer
+})
+
+middleware = pydux.apply_middleware(log_middleware, pydux_thunk.middleware)
+
+store = pydux.create_store(root_reducer, initial_state, middleware)
+
+def my_subscriber():
+	print('Current state', store.get_state())
+
+store.subscribe(my_subscriber)
+
+print('starting.')
+store.dispatch(counter.ActionCreators.increment())
+store.dispatch(counter.ActionCreators.increment())
+store.dispatch(counter.ActionCreators.increment())
+store.dispatch(calculate.ActionCreators.calculate(3, 4))
+#store.dispatch(my_thunk(4))
+time.sleep(2)
+#store.dispatch(ActionCreators.decrement())
+#store.dispatch(my_thunk(3))
+time.sleep(1)
+#store.dispatch(my_bump())
+while loop.is_running():
+	time.sleep(1)
+print('Loop done, closing task.')
+t.join()
+print('Task finished, bye!')

+ 19 - 0
pydux_thunk.py

@@ -0,0 +1,19 @@
+import asyncio
+import inspect
+
+def middleware(store):
+	dispatch = store['dispatch']
+	get_state = store['get_state']
+
+	def wrapper(next_):
+		def thunk_dispatch(action):
+			if callable(action):
+				if inspect.iscoroutinefunction(action):
+					print('found async action')
+					return loop.call_soon_threadsafe(asyncio.ensure_future, action(dispatch, get_state))
+				else:
+					print('found sync action')
+					return action(dispatch, get_state)
+			return next_(action)
+		return thunk_dispatch
+	return wrapper

+ 1 - 0
requirements.txt

@@ -0,0 +1 @@
+pydux