Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

yasm-0.1.0a1-py2.py3-none-any.whl (13.6 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page