Skip to content

Instantly share code, notes, and snippets.

@imbellish
Last active June 3, 2017 21:03
Show Gist options
  • Save imbellish/cb6cc9fc570f156e1c27accc7816aa99 to your computer and use it in GitHub Desktop.
Save imbellish/cb6cc9fc570f156e1c27accc7816aa99 to your computer and use it in GitHub Desktop.
inspired by pycon 2017 talk: https://www.youtube.com/watch?v=MtHscXjWbVs
from __future__ import print_function
import automat
import os, sys, logging, json, time
from flask import Flask, request
from Queue import Queue
from threading import Thread
class BaseMachine(object):
_machine = automat.MethodicalMachine()
q = Queue()
def __init__(self):
self._enabled = True # set your enable condition here
if self._enabled:
self.enable()
else:
self.disable()
# set up a single consumer for running tasks
worker = Thread(target=self.worker, args=(self.q,))
worker.setDaemon(True)
worker.start()
def worker(self, q):
"A worker method that executes tasks"
while True:
method = q.get()
print(method.__name__, 'returned', method())
q.task_done()
##########
# STATES #
##########
@_machine.state(serialized='init', initial=True)
def initial(self):
"Initializing state"
@_machine.state(serialized='disabled')
def disabled(self):
"Disabled state"
@_machine.state(serialized='busy')
def busy(self):
"Busy doing core things"
@_machine.state(serialized='idle')
def idle_state(self):
"waiting for command or task"
################################
# INPUTS - METHODS YOU CAN USE #
################################
@_machine.input()
def enable(self):
"Enter an enabled state"
@_machine.input()
def idle(self):
"Sets to an idle state"
@_machine.input()
def disable(self):
"Enter a disabled state"
@_machine.input()
def hello_world(self):
"Say hello"
########################
# OUTPUTS - WORKHORSES #
########################
@_machine.output()
def _disabled(self):
self._enabled = False
self._since = time.time()
return True
@_machine.output()
def _enabled(self):
self._enabled = True
self._since = time.time()
return True
@_machine.output()
def _busy(self):
self._busy = True
self._since = time.time()
return True
@_machine.output()
def _idle(self):
self._busy = False
self._since = time.time()
return True
@_machine.output()
def _hello_world(self):
print("Hello world!")
# reset the state to idle after finishing,
# or use an input to move to a new task
self.idle()
############################
# SERIALIZER - FOR CLIENTS #
############################
@_machine.serializer()
def get_state(self, state):
return { 'state': state,
'since': self._since }
# set initial state
initial.upon(enable, enter=idle_state, outputs=[_enabled])
initial.upon(disable, enter=disabled, outputs=[_disabled])
# allow enable, disable at any time, regardless of enabled/disable state
idle_state.upon(disable, enter=disabled, outputs=[_disabled])
idle_state.upon(enable, enter=idle_state, outputs=[_enabled])
disabled.upon(enable, enter=idle_state, outputs=[_enabled])
disabled.upon(disable, enter=disabled, outputs=[_disabled])
# only idle() is able to switch from busy to idle_state
busy.upon(idle, enter=idle_state, outputs=[_enabled])
# add your methods here
idle_state.upon(hello_world, enter=busy, outputs=[_hello_world])
if __name__ == '__main__':
mgr = BaseMachine()
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def status():
return json.dumps(mgr.get_state())
@app.route('/<input>', methods=['GET', 'POST'])
def command(input):
"""
Corresponds to a machine input methods
"""
if request.method == 'POST':
if input not in dir(mgr):
return json.dumps({'error': 'Invalid command'})
method = getattr(mgr, input)
mgr.q.put(method)
time.sleep(0.1)
return json.dumps(mgr.get_state())
else:
return json.dumps(mgr.get_state())
print("Try these commands in a separate terminal:")
print("import requests")
print("# Get the status of the state machine...")
print("requests.get('http://localhost:5000/').json()")
print("# Disable the state machine...")
print("requests.post('http://localhost:5000/disable').json()")
print("# Re-enable the machine...")
print("requests.post('http://localhost:5000/enable').json()")
print("# Say hello")
print("requests.post('http://localhost:5000/hello_world').json()")
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment