def test_property_initial(self): states = ['A', 'B', 'C', 'D'] # Define with list of dictionaries transitions = [ {'trigger': 'walk', 'source': 'A', 'dest': 'B'}, {'trigger': 'run', 'source': 'B', 'dest': 'C'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'D'} ] m = Machine(states=states, transitions=transitions, initial='A') self.assertEquals(m.initial, 'A') m = Machine(states=states, transitions=transitions, initial='C') self.assertEquals(m.initial, 'C') m = Machine(states=states, transitions=transitions) self.assertEquals(m.initial, 'initial')
def test_use_machine_as_model(self): states = ['A', 'B', 'C', 'D'] m = Machine(states=states, initial='A') m.add_transition('move', 'A', 'B') m.add_transition('move_to_C', 'B', 'C') m.move() self.assertEquals(m.state, 'B')
def __init__(self, datasource, criteria, kind): self.datasource = datasource self.criteria = criteria self.kind = kind self.maxcount = 50 self.hits = -1 self.querycount = 0 self.machine = Machine(model=self, states=QueryDateRangeNarrower.states, initial='asleep') self.machine.add_transition('start', '*', 'init', after='work') self.machine.add_transition('check', '*', 'finished', conditions=['is_ready']) self.machine.add_transition('step', 'init', 'finished', conditions='is_century_out_of_bounds') self.machine.add_transition('step', 'init', 'whole', after=['runquery', 'check']) self.machine.add_transition('step', 'whole', 'init', conditions='no_hits', after='range_next_century') if self.kind == self.OLDEST: self.date_from = Arrow.fromdatetime(datetime.datetime(1800, 01, 01)) self.date_to = Arrow.fromdatetime(datetime.datetime(1899, 12, 31)) self.factor = +1 self.machine.add_transition('step', 'whole', 'left', unless='is_ready', after=['range_whole_left', 'runquery', 'check']) self.machine.add_transition('step', 'left', 'right', conditions='no_hits', after=['range_left_right', 'runquery', 'check']) self.machine.add_transition('step', 'left', 'whole', unless='is_ready', after=['range_shrink']) self.machine.add_transition('step', 'right', 'whole', unless='is_ready', after=['range_shrink']) elif self.kind == self.NEWEST: self.date_from = Arrow.fromdatetime(datetime.datetime(2000, 01, 01)) self.date_to = Arrow.utcnow() self.date_to += relativedelta(months=12-self.date_to.month, days=31-self.date_to.day) self.factor = -1 self.machine.add_transition('step', 'whole', 'right', unless='is_ready', after=['range_whole_right', 'runquery', 'check']) self.machine.add_transition('step', 'right', 'left', conditions='no_hits', after=['range_right_left', 'runquery', 'check']) self.machine.add_transition('step', 'right', 'whole', unless='is_ready', after=['range_shrink']) self.machine.add_transition('step', 'left', 'whole', unless='is_ready', after=['range_shrink']) else: raise ValueError('kind must be self.OLDEST or self.NEWEST') self.delta = (self.date_to - self.date_from) / 2
def test_transition_definitions(self): states = ['A', 'B', 'C', 'D'] # Define with list of dictionaries transitions = [ {'trigger': 'walk', 'source': 'A', 'dest': 'B'}, {'trigger': 'run', 'source': 'B', 'dest': 'C'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'D'} ] m = Machine(states=states, transitions=transitions, initial='A') m.walk() self.assertEquals(m.state, 'B') # Define with list of lists transitions = [ ['walk', 'A', 'B'], ['run', 'B', 'C'], ['sprint', 'C', 'D'] ] m = Machine(states=states, transitions=transitions, initial='A') m.to_C() m.sprint() self.assertEquals(m.state, 'D')
def test_generic_callbacks(self): m = Machine(None, states=['A', 'B'], before_state_change='before_state_change', after_state_change='after_state_change', send_event=True, initial='A', auto_transitions=True) m.before_state_change = MagicMock() m.after_state_change = MagicMock() m.to_B() self.assertTrue(m.before_state_change.called) self.assertTrue(m.after_state_change.called)
def test_send_event_data(self): states = ['A', 'B', 'C', 'D'] s = Stuff() # First pass positional and keyword args directly to the callback m = Machine(model=s, states=states, initial='A', send_event=False) m.add_transition( trigger='advance', source='A', dest='B', before='set_message') s.advance(message='Hallo. My name is Inigo Montoya.') self.assertTrue(s.message.startswith('Hallo.')) # Now wrap arguments in an EventData instance m.send_event = True m.add_transition( trigger='advance', source='B', dest='C', before='extract_message') s.advance(message='You killed my father. Prepare to die.') self.assertTrue(s.message.startswith('You'))
def test_init_machine_with_hella_arguments(self): states = [ State('State1'), 'State2', { 'name': 'State3', 'on_enter': 'hello_world' } ] transitions = [ {'trigger': 'advance', 'source': 'State2', 'dest': 'State3' } ] s = Stuff() Machine( model=s, states=states, transitions=transitions, initial='State2') s.advance() self.assertEquals(s.message, 'Hello World!')
class QueryDateRangeNarrower(object): states = ['init', 'whole', 'left', 'right', 'finished', 'asleep'] OLDEST = 1 NEWEST = 2 def __init__(self, datasource, criteria, kind): self.datasource = datasource self.criteria = criteria self.kind = kind self.maxcount = 50 self.hits = -1 self.querycount = 0 self.machine = Machine(model=self, states=QueryDateRangeNarrower.states, initial='asleep') self.machine.add_transition('start', '*', 'init', after='work') self.machine.add_transition('check', '*', 'finished', conditions=['is_ready']) self.machine.add_transition('step', 'init', 'finished', conditions='is_century_out_of_bounds') self.machine.add_transition('step', 'init', 'whole', after=['runquery', 'check']) self.machine.add_transition('step', 'whole', 'init', conditions='no_hits', after='range_next_century') if self.kind == self.OLDEST: self.date_from = Arrow.fromdatetime( datetime.datetime(1800, 0o1, 0o1)) self.date_to = Arrow.fromdatetime(datetime.datetime(1899, 12, 31)) self.factor = +1 self.machine.add_transition( 'step', 'whole', 'left', unless='is_ready', after=['range_whole_left', 'runquery', 'check']) self.machine.add_transition( 'step', 'left', 'right', conditions='no_hits', after=['range_left_right', 'runquery', 'check']) self.machine.add_transition('step', 'left', 'whole', unless='is_ready', after=['range_shrink']) self.machine.add_transition('step', 'right', 'whole', unless='is_ready', after=['range_shrink']) elif self.kind == self.NEWEST: self.date_from = Arrow.fromdatetime( datetime.datetime(2000, 0o1, 0o1)) self.date_to = Arrow.utcnow() self.date_to += relativedelta(months=12 - self.date_to.month, days=31 - self.date_to.day) self.factor = -1 self.machine.add_transition( 'step', 'whole', 'right', unless='is_ready', after=['range_whole_right', 'runquery', 'check']) self.machine.add_transition( 'step', 'right', 'left', conditions='no_hits', after=['range_right_left', 'runquery', 'check']) self.machine.add_transition('step', 'right', 'whole', unless='is_ready', after=['range_shrink']) self.machine.add_transition('step', 'left', 'whole', unless='is_ready', after=['range_shrink']) else: raise ValueError('kind must be self.OLDEST or self.NEWEST') self.delta = (self.date_to - self.date_from) / 2 def runquery(self): criteria = self.criteria.copy() criteria['pubdate'] = 'within {date_from},{date_to}'.format( date_from=self.date_from.format('YYYY-MM-DD'), date_to=self.date_to.format('YYYY-MM-DD')) query = make_expression_filter({ 'datasource': self.datasource, 'format': 'comfort', 'criteria': criteria, })['expression'] if self.datasource == 'ops': self.response, self.hits = query_ops(query, limit=self.maxcount) elif self.datasource == 'depatisnet': self.response, self.hits = query_depatisnet(query, limit=self.maxcount) elif self.datasource == 'sip': self.response, self.hits = query_sip(query, limit=self.maxcount) else: raise ValueError('Data source "{0}" not implemented'.format( self.datasource)) self.querycount += 1 def no_hits(self): return self.hits == 0 def is_ready(self): return self.hits > 0 and self.hits <= self.maxcount # for "oldest" searches def range_whole_left(self): self.date_to -= self.delta def range_left_right(self): self.date_from += self.delta self.date_to += self.delta # for "newest" searches def range_whole_right(self): self.date_from += self.delta def range_right_left(self): self.date_from -= self.delta self.date_to -= self.delta def range_shrink(self): self.delta /= 2 def range_next_century(self): century = self.date_from.year / 100 century += self.factor year_begin = century * 100 + 00 year_end = century * 100 + 99 self.date_from += relativedelta(years=year_begin - self.date_from.year, months=-self.date_from.month + 1, days=-self.date_from.day + 1) self.date_to += relativedelta(years=year_end - self.date_to.year, months=12 - self.date_to.month, days=31 - self.date_to.day) def is_century_out_of_bounds(self): return self.date_from.year > Arrow.utcnow( ).year or self.date_to.year < 1800 def work(self): debug = False while True: if debug: print('-' * 42) print('state:', self.state) print('delta:', self.delta) print('querycount:', self.querycount) if self.state == 'finished' or self.querycount > 15: break self.step()
def __init__(self, states, initial='A'): self.state = None Machine.__init__(self, states=states, initial=initial)
def test_ignore_invalid_triggers(self): a_state = State('A') transitions = [['a_to_b', 'A', 'B']] # Exception is triggered by default b_state = State('B') m1 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B') with self.assertRaises(MachineError): m1.a_to_b() # Exception is suppressed, so this passes b_state = State('B', ignore_invalid_triggers=True) m2 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B') m2.a_to_b() # Set for some states but not others new_states = ['C', 'D'] m1.add_states(new_states, ignore_invalid_triggers=True) m1.to_D() m1.a_to_b() # passes because exception suppressed for D m1.to_B() with self.assertRaises(MachineError): m1.a_to_b() # Set at machine level m3 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B', ignore_invalid_triggers=True) m3.a_to_b()
def test_ordered_transitions(self): states = ['beginning', 'middle', 'end'] m = Machine(None, states) m.add_ordered_transitions() self.assertEquals(m.state, 'initial') m.next_state() self.assertEquals(m.state, 'beginning') m.next_state() m.next_state() self.assertEquals(m.state, 'end') m.next_state() self.assertEquals(m.state, 'initial') # Include initial state in loop m = Machine(None, states) m.add_ordered_transitions(loop_includes_initial=False) m.to_end() m.next_state() self.assertEquals(m.state, 'beginning') # Test user-determined sequence and trigger name m = Machine(None, states, initial='beginning') m.add_ordered_transitions(['end', 'beginning'], trigger='advance') m.advance() self.assertEquals(m.state, 'end') m.advance() self.assertEquals(m.state, 'beginning') # Via init argument m = Machine( None, states, initial='beginning', ordered_transitions=True) m.next_state() self.assertEquals(m.state, 'middle')
def test_auto_transitions(self): states = ['A', {'name': 'B'}, State(name='C')] m = Machine(None, states, initial='A', auto_transitions=True) m.to_B() self.assertEquals(m.state, 'B') m.to_C() self.assertEquals(m.state, 'C') m.to_A() self.assertEquals(m.state, 'A') # Should fail if auto transitions is off... m = Machine(None, states, initial='A', auto_transitions=False) with self.assertRaises(TypeError): m.to_C()
def __init__(self): self.state = None states = ['A', 'B', 'C', 'D', 'E', 'F'] self.machine = Machine(self, states=states, initial='A')