Python State Machines for Humans
Project description
state machine for humans
There are two types of developers in this world: those who love state machines and those who will eventually.
I fall in the first camp. I think it is really important to have a declarative way to define the states of an object. That’s why I developed yasm.
Install
pip install yasm
Basic Usage
from collections import deque
import operator
import six
from string import whitespace, digits
from yasm import Event, state_machine
from yasm.utils import dispatch
@state_machine('calculator')
class Calculator(object):
operators = {
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
}
if six.PY3:
operators['/'] = operator.truediv
else:
operators['/'] = operator.div
def __init__(self):
self.stack = deque()
self.result = None
def reset(self):
self.stack.clear()
self.result = None
self.machine.reinit_instance(self)
def calculate(self, string):
self.reset()
for char in string:
dispatch(self, Event('parse', input=char))
return self.result
def start_building_number(self, state, event, instance):
digit = event.input
self.stack.append(int(digit))
def build_number(self, state, event, instance):
digit = event.input
number = str(self.stack.pop())
number += digit
self.stack.append(int(number))
def do_operation(self, state, event, instance):
operation = event.input
y = self.stack.pop()
x = self.stack.pop()
self.stack.append(self.operators[operation](float(x), float(y)))
def do_equal(self, state, event, instance):
number = self.stack.pop()
self.result = number
def is_digit(state, event, instance):
return event.input in digits
sm = Calculator.machine
sm.add_states(['initial', 'number', 'result'], initial='initial')
sm.add_transitions([
{'from_state': 'initial', 'to_state': 'number', 'event': 'parse',
'conditions': [is_digit], 'before': 'start_building_number'},
{'from_state': 'number', 'to_state': 'number', 'event': 'parse',
'conditions': [is_digit], 'before': 'build_number'},
{'from_state': 'number', 'to_state': 'initial', 'event': 'parse',
'conditions': [lambda state, evt, ins: evt.input in whitespace]},
{'from_state': 'initial', 'to_state': 'initial', 'event': 'parse',
'conditions': [lambda state, evt, ins: evt.input in '+-*/'],
'before': 'do_operation'},
{'from_state': 'initial', 'to_state': 'result', 'event': 'parse',
'conditions': [lambda state, evt, ins: evt.input == '='],
'before': 'do_equal'},
])
calc = Calculator()
for syntax, value in ((' 167 3 2 2 * * * 1 - =', 2003),
(' 167 3 2 2 * * * 1 - 2 / =', 1001.5),
(' 3 5 6 + * =', 33),
(' 3 4 + =', 7),
('2 4 / 5 6 - * =', -0.5),):
result = calc.calculate(syntax)
assert result == value, (syntax, result, value)
calc.reset()
Thank you
to aasm and ruby’s state_machine and jtushman’s jtushman/state_machine and all other state machines that I loved before
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yasm-0.1.0a1-py2.py3-none-any.whl.
File metadata
- Download URL: yasm-0.1.0a1-py2.py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/20.7.0 requests-toolbelt/0.8.0 tqdm/4.24.0 CPython/2.7.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
268aaee7a0e35da90183687c8393a41cba6d2bccfaecd37905386ede9e81976c
|
|
| MD5 |
8d61c33752e5f9844fda039ba9098437
|
|
| BLAKE2b-256 |
e3a23a9e6b11dad093dcd0232b565b06fefa97e8c40bf7b834bb123dd989dc46
|