Example #1
0
 def add_transition(self,
                    event_name,
                    initial_states,
                    resulting_state,
                    pre_hooks=None,
                    post_hooks=None,
                    error_hooks=None):
     """
     Declare a transition that is valid only if the current state is one of `initial_states`.
     The state after the transition will be `resulting_state`.
     :param event_name:
     :param initial_states: Can be a string if single state or a list if many states. Can accept wildcard "*" for all possible states.
     :param resulting_state:
     :return:
     """
     event_name = self.name_for_event(event_name)
     initial_states = [
         self.name_for_state(s) for s in listify(initial_states)
     ]
     resulting_state = self.name_for_state(resulting_state)
     # Not adding self._pre_hooks or self._post_hooks to `add_transition` for better control.
     # Otherwise self._pre_hooks+listify(pre_hooks) etc can be done.
     self._machine.add_transition(event_name,
                                  source=initial_states,
                                  dest=resulting_state,
                                  before=listify(pre_hooks),
                                  after=listify(post_hooks))
Example #2
0
 def set_state(self, states, model=None):
     """ Set the current state.
     Args:
         states (list of str or Enum or State): value of state(s) to be set
         model (optional[object]): targeted model; if not set, all models will be set to 'state'
     """
     values = [self._set_state(value) for value in listify(states)]
     models = self.models if model is None else listify(model)
     for mod in models:
         setattr(mod, self.model_attribute,
                 values if len(values) > 1 else values[0])
Example #3
0
    def test_listify(self):
        self.assertEqual(listify(4), [4])
        self.assertEqual(listify(None), [])
        self.assertEqual(listify((4, 5)), (4, 5))
        self.assertEqual(listify([1, 3]), [1, 3])

        class Foo:
            pass

        obj = Foo()
        proxy = weakref.proxy(obj)
        del obj
        self.assertEqual(listify(proxy), [proxy])
Example #4
0
    def add_model(self, model, *args, **kwargs):
        models = listify(model)

        try:
            model_context = listify(kwargs.pop('model_context'))
        except KeyError:
            model_context = []

        output = super(LockedMachine, self).add_model(models, *args, **kwargs)

        for model in models:
            self.model_context_map[model].extend(self.machine_context)
            self.model_context_map[model].extend(model_context)

        return output
Example #5
0
 def _update_model(event_data, tree):
     model_states = _build_state_list(
         tree, event_data.machine.state_cls.separator)
     with event_data.machine():
         event_data.machine.set_state(model_states, event_data.model)
         states = event_data.machine.get_states(listify(model_states))
         event_data.state = states[0] if len(states) == 1 else states
Example #6
0
    def remove_model(self, model, *args, **kwargs):
        models = listify(model)

        for model in models:
            del self.model_context_map[model]

        return super(LockedMachine, self).add_model(models, *args, **kwargs)
 def _change_state(self, event_data):
     graph = event_data.machine.model_graphs[event_data.model]
     graph.reset_styling()
     graph.set_previous_transition(self.source, self.dest, event_data.event.name)
     _super(TransitionGraphSupport, self)._change_state(event_data)  # pylint: disable=protected-access
     for state in _flatten(listify(getattr(event_data.model, event_data.machine.model_attribute))):
         graph.set_node_style(self.dest if hasattr(state, 'name') else state, 'active')
Example #8
0
    def add_model(self, model, *args, **kwargs):
        models = listify(model)

        try:
            model_context = listify(kwargs.pop('model_context'))
        except KeyError:
            model_context = []

        output = _super(LockedMachine, self).add_model(models, *args, **kwargs)

        for model in models:
            model = self if model == 'self' else model
            self.model_context_map[model].extend(self.machine_context)
            self.model_context_map[model].extend(model_context)

        return output
Example #9
0
 def add_substates(self, states):
     """ Adds a list of states to the current state.
     Args:
         states (list): List of states to add to the current state.
     """
     for state in listify(states):
         self.states[state.name] = state
Example #10
0
    def remove_model(self, model, *args, **kwargs):
        models = listify(model)

        for model in models:
            del self.model_context_map[model]

        return super(LockedMachine, self).remove_model(models, *args, **kwargs)
Example #11
0
    def remove_model(self, model):
        """ Extends `transitions.core.Machine.remove_model` by removing model specific context maps
            from the machine when the model itself is removed. """
        models = listify(model)

        for mod in models:
            del self.model_context_map[mod]

        return _super(LockedMachine, self).remove_model(models)
Example #12
0
    def remove_model(self, model):
        """ Extends `transitions.core.Machine.remove_model` by removing model specific context maps
            from the machine when the model itself is removed. """
        models = listify(model)

        for mod in models:
            del self.model_context_map[mod]

        return _super(LockedMachine, self).remove_model(models)
 def add_model(self, model, initial=None):
     models = listify(model)
     super(GraphMachine, self).add_model(models, initial)
     for mod in models:
         mod = self if mod == 'self' else mod
         if hasattr(mod, 'get_graph'):
             raise AttributeError('Model already has a get_graph attribute. Graph retrieval cannot be bound.')
         setattr(mod, 'get_graph', partial(self._get_graph, mod))
         _ = mod.get_graph(title=self.title, force_new=True)  # initialises graph
Example #14
0
    def add_model(self, model, initial=None, model_context=None):
        """ Extends `transitions.core.Machine.add_model` by `model_context` keyword.
        Args:
            model (list or object): A model (list) to be managed by the machine.
            initial (string or State): The initial state of the passed model[s].
            model_context (list or object): If passed, assign the context (list) to the machines
                model specific context map.
        """
        models = listify(model)
        model_context = listify(model_context) if model_context is not None else []
        output = _super(LockedMachine, self).add_model(models, initial)

        for mod in models:
            mod = self if mod == 'self' else mod
            self.model_context_map[mod].extend(self.machine_context)
            self.model_context_map[mod].extend(model_context)

        return output
Example #15
0
 def _enter_nested(self, root, dest, prefix_path, event_data):
     if root:
         state_name = root.pop(0)
         with event_data.machine(state_name):
             return self._enter_nested(root, dest, prefix_path, event_data)
     elif dest:
         new_states = OrderedDict()
         state_name = dest.pop(0)
         with event_data.machine(state_name):
             new_states[state_name], new_enter = self._enter_nested(
                 [], dest, prefix_path + [state_name], event_data)
             enter_partials = [
                 partial(event_data.machine.scoped.scoped_enter, event_data,
                         prefix_path)
             ] + new_enter
         return new_states, enter_partials
     elif event_data.machine.scoped.initial:
         new_states = OrderedDict()
         enter_partials = []
         q = []
         prefix = prefix_path
         scoped_tree = new_states
         initial_states = [
             event_data.machine.scoped.states[i]
             for i in listify(event_data.machine.scoped.initial)
         ]
         while True:
             event_data.scope = prefix
             for state in initial_states:
                 enter_partials.append(
                     partial(state.scoped_enter, event_data, prefix))
                 scoped_tree[state.name] = OrderedDict()
                 if state.initial:
                     q.append(
                         (scoped_tree[state.name], prefix + [state.name],
                          [state.states[i]
                           for i in listify(state.initial)]))
             if not q:
                 break
             scoped_tree, prefix, initial_states = q.pop(0)
         return new_states, enter_partials
     else:
         return {}, []
Example #16
0
    def add_model(self, model, initial=None, model_context=None):
        """ Extends `transitions.core.Machine.add_model` by `model_context` keyword.
        Args:
            model (list or object): A model (list) to be managed by the machine.
            initial (str, Enum or State): The initial state of the passed model[s].
            model_context (list or object): If passed, assign the context (list) to the machines
                model specific context map.
        """
        models = listify(model)
        model_context = listify(
            model_context) if model_context is not None else []
        output = _super(LockedMachine, self).add_model(models, initial)

        for mod in models:
            mod = self if mod == 'self' else mod
            self.model_context_map[mod].extend(self.machine_context)
            self.model_context_map[mod].extend(model_context)

        return output
Example #17
0
    def __init__(self, *args, **kwargs):
        self._locked = 0

        try:
            self.machine_context = listify(kwargs.pop('machine_context'))
        except KeyError:
            self.machine_context = [PicklableLock()]

        self.machine_context.append(self)
        self.model_context_map = defaultdict(list)

        _super(LockedMachine, self).__init__(*args, **kwargs)
Example #18
0
    def __init__(self, *args, **kwargs):
        self._locked = 0

        try:
            self.machine_context = listify(kwargs.pop('machine_context'))
        except KeyError:
            self.machine_context = [PickleableLock()]

        self.machine_context.append(self)
        self.model_context_map = defaultdict(list)

        _super(LockedMachine, self).__init__(*args, **kwargs)
Example #19
0
    def __init__(self, *args, **kwargs):
        try:
            self.machine_context = listify(kwargs.pop('machine_context'))
        except KeyError:
            self.machine_context = [RLock()]

        self.model_context_map = defaultdict(list)

        super(LockedMachine, self).__init__(*args, **kwargs)

        if self.machine_context:
            for model in self.models:
                self.model_context_map[model].extend(self.machine_context)
Example #20
0
 def add_model(self, model, initial=None):
     """ Extends transitions.core.Machine.add_model by applying a custom 'to' function to
         the added model.
     """
     _super(HierarchicalMachine, self).add_model(model, initial=initial)
     models = listify(model)
     for mod in models:
         mod = self if mod == 'self' else mod
         # TODO: Remove 'mod != self' in 0.7.0
         if hasattr(mod, 'to') and mod != self:
             _LOGGER.warning(
                 "%sModel already has a 'to'-method. It will NOT "
                 "be overwritten by NestedMachine", self.name)
         else:
             to_func = partial(self.to_state, mod)
             setattr(mod, 'to', to_func)
Example #21
0
 def _get_graph(self, model, title=None, force_new=False, show_roi=False):
     if force_new:
         grph = self.graph_cls(self, title=title if title is not None else self.title)
         self.model_graphs[model] = grph
         try:
             for state in _flatten(listify(getattr(model, self.model_attribute))):
                 grph.set_node_style(self.dest if hasattr(state, 'name') else state, 'active')
         except AttributeError:
             _LOGGER.info("Could not set active state of diagram")
     try:
         m = self.model_graphs[model]
     except KeyError:
         _ = self._get_graph(model, title, force_new=True)
         m = self.model_graphs[model]
     m.roi_state = getattr(model, self.model_attribute) if show_roi else None
     return m.get_graph(title=title)
Example #22
0
 def _trigger_event(self, _model, _trigger, _state_tree, *args, **kwargs):
     if _state_tree is None:
         _state_tree = self._build_state_tree(
             listify(getattr(_model, self.model_attribute)),
             self.state_cls.separator)
     res = {}
     for key, value in _state_tree.items():
         if value:
             with self(key):
                 res[key] = self._trigger_event(_model, _trigger, value,
                                                *args, **kwargs)
         if not res.get(key, None) and _trigger in self.events:
             res[key] = self.events[_trigger].trigger(
                 _model, self, *args, **kwargs)
     return None if not res or all(
         v is None for v in res.values()) else any(res.values())
Example #23
0
 def _resolve_initial(self, models, state_name_path, prefix=[]):
     if state_name_path:
         state_name = state_name_path.pop(0)
         with self(state_name):
             return self._resolve_initial(models,
                                          state_name_path,
                                          prefix=prefix + [state_name])
     if self.scoped.initial:
         entered_states = []
         for initial_state_name in listify(self.scoped.initial):
             with self(initial_state_name):
                 entered_states.append(
                     self._resolve_initial(models, [],
                                           prefix=prefix +
                                           [self.scoped.name]))
         return entered_states if len(
             entered_states) > 1 else entered_states[0]
     return self.state_cls.separator.join(prefix)
Example #24
0
    def _resolve_transition(self, event_data):
        machine = event_data.machine
        dst_name_path = machine.get_local_name(self.dest, join=False)
        _ = machine.get_state(dst_name_path)
        model_states = listify(
            getattr(event_data.model, machine.model_attribute))
        state_tree = machine._build_state_tree(model_states,
                                               machine.state_cls.separator)

        scope = machine.get_global_name(join=False)
        src_name_path = event_data.source_path
        if src_name_path == dst_name_path:
            root = src_name_path[:-1]  # exit and enter the same state
        else:
            root = []
            while dst_name_path and src_name_path and src_name_path[
                    0] == dst_name_path[0]:
                root.append(src_name_path.pop(0))
                dst_name_path.pop(0)

        scoped_tree = reduce(dict.get, scope + root, state_tree)
        exit_partials = []
        if src_name_path:
            for state_name in _resolve_order(scoped_tree):
                cb = partial(
                    machine.get_state(root + state_name).scoped_exit,
                    event_data, scope + root + state_name[:-1])
                exit_partials.append(cb)
        if dst_name_path:
            new_states, enter_partials = self._enter_nested(
                root, dst_name_path, scope + root, event_data)
        else:
            new_states, enter_partials = {}, []

        for key in scoped_tree:
            del scoped_tree[key]

        for new_key, value in new_states.items():
            scoped_tree[new_key] = value
            break

        return state_tree, exit_partials, enter_partials
Example #25
0
 def add_model(self, model, initial=None):
     """ Extends transitions.core.Machine.add_model by applying a custom 'to' function to
         the added model.
     """
     models = [mod if mod != 'self' else self for mod in listify(model)]
     _super(HierarchicalMachine, self).add_model(models, initial=initial)
     initial_name = getattr(models[0], self.model_attribute)
     if hasattr(initial_name, 'name'):
         initial_name = initial_name.name
     initial_states = self._resolve_initial(
         models, initial_name.split(self.state_cls.separator))
     for mod in models:
         self.set_state(initial_states, mod)
         if hasattr(mod, 'to'):
             _LOGGER.warning(
                 "%sModel already has a 'to'-method. It will NOT "
                 "be overwritten by NestedMachine", self.name)
         else:
             to_func = partial(self.to_state, mod)
             setattr(mod, 'to', to_func)
Example #26
0
 def add_transition(self,
                    trigger,
                    source,
                    dest,
                    conditions=None,
                    unless=None,
                    before=None,
                    after=None,
                    prepare=None,
                    **kwargs):
     if source != self.wildcard_all:
         source = [
             self.state_cls.separator.join(self._get_enum_path(s))
             if isinstance(s, Enum) else s for s in listify(source)
         ]
     if dest != self.wildcard_same:
         dest = self.state_cls.separator.join(
             self._get_enum_path(dest)) if isinstance(dest, Enum) else dest
     _super(HierarchicalMachine,
            self).add_transition(trigger, source, dest, conditions, unless,
                                 before, after, prepare, **kwargs)
 def on_timeout(self, value):
     """ Listifies passed values and assigns them to on_timeout."""
     self._on_timeout = listify(value)
Example #28
0
 def _add_error_hooks(self, error_hooks):
     self._error_hooks += listify(error_hooks)
Example #29
0
 def test_listify(self):
     self.assertEquals(listify(4), [4])
     self.assertEquals(listify(None), [])
     self.assertEquals(listify((4, 5)), (4, 5))
     self.assertEquals(listify([1, 3]), [1, 3])
Example #30
0
 def _add_post_hooks(self, post_hooks):
     self._post_hooks += listify(post_hooks)
Example #31
0
    def add_states(self,
                   states,
                   on_enter=None,
                   on_exit=None,
                   ignore_invalid_triggers=None,
                   **kwargs):
        """ Add new nested state(s).
        Args:
            states (list, str, dict, Enum or NestedState): a list, a NestedState instance, the
                name of a new state, an enumeration (member) or a dict with keywords to pass on to the
                NestedState initializer. If a list, each element can be a string, NestedState or enumeration member.
            on_enter (str or list): callbacks to trigger when the state is
                entered. Only valid if first argument is string.
            on_exit (str or list): callbacks to trigger when the state is
                exited. Only valid if first argument is string.
            ignore_invalid_triggers: when True, any calls to trigger methods
                that are not valid for the present state (e.g., calling an
                a_to_b() trigger when the current state is c) will be silently
                ignored rather than raising an invalid transition exception.
                Note that this argument takes precedence over the same
                argument defined at the Machine level, and is in turn
                overridden by any ignore_invalid_triggers explicitly
                passed in an individual state's initialization arguments.
            **kwargs additional keyword arguments used by state mixins.
        """
        remap = kwargs.pop('remap', None)
        for state in listify(states):
            if isinstance(state, Enum) and isinstance(state.value, EnumMeta):
                state = {'name': state.name, 'children': state.value}

            if isinstance(state, string_types):
                if remap is not None and state in remap:
                    return
                domains = state.split(self.state_cls.separator, 1)
                if len(domains) > 1:
                    try:
                        self.get_state(domains[0])
                    except ValueError:
                        self.add_state(
                            domains[0],
                            on_enter=on_enter,
                            on_exit=on_exit,
                            ignore_invalid_triggers=ignore_invalid_triggers,
                            **kwargs)
                    with self(domains[0]):
                        self.add_states(
                            domains[1],
                            on_enter=on_enter,
                            on_exit=on_exit,
                            ignore_invalid_triggers=ignore_invalid_triggers,
                            **kwargs)
                else:
                    if state in self.states:
                        raise ValueError(
                            "State %s cannot be added since it already exists."
                            % (state, ))
                    new_state = self._create_state(state)
                    self.states[new_state.name] = new_state
                    self._init_state(new_state)
            elif isinstance(state, Enum):
                if remap is not None and state.name in remap:
                    return
                new_state = self._create_state(state)
                if state.name in self.states:
                    raise ValueError(
                        "State %s cannot be added since it already exists." %
                        (state.name, ))
                self.states[new_state.name] = new_state
                self._init_state(new_state)
            elif isinstance(state, dict):
                if remap is not None and state['name'] in remap:
                    return
                state = state.copy(
                )  # prevent messing with the initially passed dict
                remap = state.pop('remap', None)
                state_children = state.pop('children', [])
                state_parallel = state.pop('parallel', [])
                transitions = state.pop('transitions', [])
                new_state = self._create_state(**state)
                self.states[new_state.name] = new_state
                self._init_state(new_state)
                remapped_transitions = []
                with self(new_state.name):
                    if state_parallel:
                        self.add_states(state_parallel, remap=remap, **kwargs)
                        new_state.initial = [
                            s if isinstance(s, string_types) else s['name']
                            for s in state_parallel
                        ]
                    else:
                        self.add_states(state_children, remap=remap, **kwargs)
                    if remap is not None:
                        drop_event = []
                        for evt in self.events.values():
                            self.events[evt.name] = copy.copy(evt)
                        for trigger, event in self.events.items():
                            drop_source = []
                            event.transitions = copy.deepcopy(
                                event.transitions)
                            for source_name, trans_source in event.transitions.items(
                            ):
                                if source_name in remap:
                                    drop_source.append(source_name)
                                    continue
                                drop_trans = []
                                for trans in trans_source:
                                    if trans.dest in remap:
                                        conditions, unless = [], []
                                        for cond in trans.conditions:
                                            # split a list in two lists based on the accessors (cond.target) truth value
                                            (unless,
                                             conditions)[cond.target].append(
                                                 cond.func)
                                        remapped_transitions.append({
                                            'trigger':
                                            trigger,
                                            'source':
                                            new_state.name +
                                            self.state_cls.separator +
                                            trans.source,
                                            'dest':
                                            remap[trans.dest],
                                            'conditions':
                                            conditions,
                                            'unless':
                                            unless,
                                            'prepare':
                                            trans.prepare,
                                            'before':
                                            trans.before,
                                            'after':
                                            trans.after
                                        })
                                        drop_trans.append(trans)
                                for t in drop_trans:
                                    trans_source.remove(t)
                                if not trans_source:
                                    drop_source.append(source_name)
                            for s in drop_source:
                                del event.transitions[s]
                            if not event.transitions:
                                drop_event.append(trigger)
                        for e in drop_event:
                            del self.events[e]
                    if transitions:
                        self.add_transitions(transitions)
                self.add_transitions(remapped_transitions)
            elif isinstance(state, NestedState):
                if state.name in self.states:
                    raise ValueError(
                        "State %s cannot be added since it already exists." %
                        (state.name, ))
                self.states[state.name] = state
                self._init_state(state)
            elif isinstance(state, Machine):
                new_states = [
                    s for s in state.states.values()
                    if remap is None or s not in remap
                ]
                self.add_states(new_states)
                for ev in state.events.values():
                    self.events[ev.name] = ev
                if self.scoped.initial is None:
                    self.scoped.initial = state.initial
            elif isinstance(state,
                            State) and not isinstance(state, NestedState):
                raise ValueError(
                    "A passed state object must derive from NestedState! "
                    "A default State object is not sufficient")
            else:
                raise ValueError("Cannot add state of type %s. " %
                                 (type(state).__name__, ))
Example #32
0
    def _traverse(self,
                  states,
                  on_enter=None,
                  on_exit=None,
                  ignore_invalid_triggers=None,
                  parent=None,
                  remap=None):
        """ Parses passed value to build a nested state structure recursively.
        Args:
            states (list, str, dict, or State): a list, a State instance, the
                name of a new state, or a dict with keywords to pass on to the
                State initializer. If a list, each element can be of any of the
                latter three types.
            on_enter (str or list): callbacks to trigger when the state is
                entered. Only valid if first argument is string.
            on_exit (str or list): callbacks to trigger when the state is
                exited. Only valid if first argument is string.
            ignore_invalid_triggers: when True, any calls to trigger methods
                that are not valid for the present state (e.g., calling an
                a_to_b() trigger when the current state is c) will be silently
                ignored rather than raising an invalid transition exception.
                Note that this argument takes precedence over the same
                argument defined at the Machine level, and is in turn
                overridden by any ignore_invalid_triggers explicitly
                passed in an individual state's initialization arguments.
            parent (NestedState or str): parent state for nested states.
            remap (dict): reassigns transitions named `key from nested machines to parent state `value`.
        Returns: list of new `NestedState` objects
        """
        states = listify(states)
        new_states = []
        ignore = ignore_invalid_triggers
        remap = {} if remap is None else remap
        parent = self.get_state(parent) if isinstance(parent,
                                                      (string_types,
                                                       Enum)) else parent

        if ignore is None:
            ignore = self.ignore_invalid_triggers
        for state in states:
            tmp_states = []
            # other state representations are handled almost like in the base class but a parent parameter is added
            if isinstance(state, (string_types, Enum)):
                if state in remap:
                    continue
                tmp_states.append(
                    self._create_state(state,
                                       on_enter=on_enter,
                                       on_exit=on_exit,
                                       parent=parent,
                                       ignore_invalid_triggers=ignore))
            elif isinstance(state, dict):
                if state['name'] in remap:
                    continue

                # shallow copy the dictionary to alter/add some parameters
                state = copy(state)
                if 'ignore_invalid_triggers' not in state:
                    state['ignore_invalid_triggers'] = ignore
                if 'parent' not in state:
                    state['parent'] = parent

                try:
                    state_children = state.pop(
                        'children')  # throws KeyError when no children set
                    state_remap = state.pop('remap', None)
                    state_parent = self._create_state(**state)
                    nested = self._traverse(state_children,
                                            parent=state_parent,
                                            remap=state_remap)
                    tmp_states.append(state_parent)
                    tmp_states.extend(nested)
                except KeyError:
                    tmp_states.insert(0, self._create_state(**state))
            elif isinstance(state, HierarchicalMachine):
                # set initial state of parent if it is None
                if parent.initial is None:
                    parent.initial = state.initial
                # (deep) copy only states not mentioned in remap
                copied_states = [
                    s for s in deepcopy(state.states).values()
                    if s.name not in remap
                ]
                # inner_states are the root states of the passed machine
                # which have be attached to the parent
                inner_states = [s for s in copied_states if s.level == 0]
                for inner in inner_states:
                    inner.parent = parent
                tmp_states.extend(copied_states)
                for trigger, event in state.events.items():
                    if trigger.startswith('to_'):
                        path = trigger[3:].split(self.state_cls.separator)
                        # do not copy auto_transitions since they would not be valid anymore;
                        # trigger and destination do not exist in the new environment
                        if path[0] in remap:
                            continue
                        ppath = parent.name.split(self.state_cls.separator)
                        path = ['to_' + ppath[0]] + ppath[1:] + path
                        trigger = '.'.join(path)
                    # (deep) copy transitions and
                    # adjust all transition start and end points to new state names
                    for transitions in deepcopy(event.transitions).values():
                        for transition in transitions:
                            src = transition.source
                            # transitions from remapped states will be filtered to prevent
                            # unexpected behaviour in the parent machine
                            if src in remap:
                                continue
                            dst = parent.name + self.state_cls.separator + transition.dest\
                                if transition.dest not in remap else remap[transition.dest]
                            conditions, unless = [], []
                            for cond in transition.conditions:
                                # split a list in two lists based on the accessors (cond.target) truth value
                                (unless,
                                 conditions)[cond.target].append(cond.func)
                            self._buffered_transitions.append({
                                'trigger':
                                trigger,
                                'source':
                                parent.name + self.state_cls.separator + src,
                                'dest':
                                dst,
                                'conditions':
                                conditions,
                                'unless':
                                unless,
                                'prepare':
                                transition.prepare,
                                'before':
                                transition.before,
                                'after':
                                transition.after
                            })

            elif isinstance(state, NestedState):
                tmp_states.append(state)
                if state.children:
                    tmp_states.extend(
                        self._traverse(
                            state.children,
                            on_enter=on_enter,
                            on_exit=on_exit,
                            ignore_invalid_triggers=ignore_invalid_triggers,
                            parent=state,
                            remap=remap))
            else:
                raise ValueError(
                    "%s is not an instance or subclass of NestedState "
                    "required by HierarchicalMachine." % state)
            new_states.extend(tmp_states)

        duplicate_check = []
        for new in new_states:
            if new.name in duplicate_check:
                # collect state names for the following error message
                state_names = [s.name for s in new_states]
                raise ValueError(
                    "State %s cannot be added since it is already in state list %s."
                    % (new.name, state_names))
            else:
                duplicate_check.append(new.name)
        return new_states
Example #33
0
 def test_listify(self):
     self.assertEqual(listify(4), [4])
     self.assertEqual(listify(None), [])
     self.assertEqual(listify((4, 5)), (4, 5))
     self.assertEqual(listify([1, 3]), [1, 3])
Example #34
0
 def _add_pre_hooks(self, pre_hooks):
     self._pre_hooks += listify(pre_hooks)