(self, states, on_enter=None, on_exit=None, ignore_invalid_triggers=None, **kwargs)
Add new nested state(s).
        Args:
            states (list, str, dict, Enum, NestedState or Machine): 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, dict, 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.
Add new nested state(s).
        Args:
            states (list, str, dict, Enum, NestedState or Machine): 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, dict, 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.
Add new nested state ( s ) . Args : states ( list str dict Enum NestedState or Machine ) : 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 dict 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 .
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, NestedState or Machine): 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, dict, 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)
        ignore = self.ignore_invalid_triggers if ignore_invalid_triggers is None else ignore_invalid_triggers

        for state in listify(states):
            if isinstance(state, Enum):
                if isinstance(state.value, EnumMeta):
                    state = {'name': state, 'children': state.value}
                elif isinstance(state.value, dict):
                    state = dict(name=state, **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 {0} cannot be added since it already exists.".format(state))
                    new_state = self._create_state(state, on_enter=on_enter, on_exit=on_exit,
                                                   ignore_invalid_triggers=ignore, **kwargs)
                    self.states[new_state.name] = new_state
                    self._init_state(new_state)
            elif isinstance(state, Enum):
                if self.state_cls.separator in state.name:
                    raise ValueError("State '{0}' contains '{1}' which is used as state name separator. "
                                     "Consider changing the NestedState.separator to avoid this issue."
                                     "".format(state.name, self.state_cls.separator))
                if remap is not None and state.name in remap:
                    return
                new_state = self._create_state(state, on_enter=on_enter, on_exit=on_exit,
                                               ignore_invalid_triggers=ignore, **kwargs)
                if state.name in self.states:
                    raise ValueError("State {0} cannot be added since it already exists.".format(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)
                if 'ignore_invalid_triggers' not in state:
                    state['ignore_invalid_triggers'] = ignore

                # parallel: [states] is just a short handle for {children: [states], initial: [state_names]}
                state_parallel = state.pop('parallel', [])
                if state_parallel:
                    state_children = state_parallel
                    state['initial'] = [s['name'] if isinstance(s, dict)
                                        else s for s in state_children]
                else:
                    state_children = state.pop('children', state.pop('states', []))
                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):
                    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 {0} cannot be added since it already exists.".format(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 {0}. ".format(type(state).__name__))
def add_states ( self , states , on_enter = None , on_exit = None , ignore_invalid_triggers = None , * * kwargs ) : remap = kwargs . pop ( 'remap' , None ) ignore = self . ignore_invalid_triggers if ignore_invalid_triggers is None else ignore_invalid_triggers for state in listify ( states ) : if isinstance ( state , Enum ) : if isinstance ( state . value , EnumMeta ) : state = { 'name' : state , 'children' : state . value } elif isinstance ( state . value , dict ) : state = dict ( name = state , * * 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 {0} cannot be added since it already exists." . format ( state ) ) new_state = self . _create_state ( state , on_enter = on_enter , on_exit = on_exit , ignore_invalid_triggers = ignore , * * kwargs ) self . states [ new_state . name ] = new_state self . _init_state ( new_state ) elif isinstance ( state , Enum ) : if self . state_cls . separator in state . name : raise ValueError ( "State '{0}' contains '{1}' which is used as state name separator. " "Consider changing the NestedState.separator to avoid this issue." "" . format ( state . name , self . state_cls . separator ) ) if remap is not None and state . name in remap : return new_state = self . _create_state ( state , on_enter = on_enter , on_exit = on_exit , ignore_invalid_triggers = ignore , * * kwargs ) if state . name in self . states : raise ValueError ( "State {0} cannot be added since it already exists." . format ( 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 ) if 'ignore_invalid_triggers' not in state : state [ 'ignore_invalid_triggers' ] = ignore # parallel: [states] is just a short handle for {children: [states], initial: [state_names]} state_parallel = state . pop ( 'parallel' , [ ] ) if state_parallel : state_children = state_parallel state [ 'initial' ] = [ s [ 'name' ] if isinstance ( s , dict ) else s for s in state_children ] else : state_children = state . pop ( 'children' , state . pop ( 'states' , [ ] ) ) 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 ) : 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 {0} cannot be added since it already exists." . format ( 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 {0}. " . format ( type ( state ) . __name__ ) )