are free to define their own channels. If a message is sent to a
channel that has not been defined or has no listeners, there is no effect.
import traceback as _traceback
- """State machine and pub/sub messenger."""
+ """State machine and pub/sub messenger.
+ If the 'select' module is present (POSIX systems), then select.select()
+ (on an os.pipe) will be used in self.wait instead of time.sleep().
publish_exception_class = ChannelFailures
def __init__(self, transitions=None, errors=None,
- initial_state=None, extra_channels=None):
+ initial_state=None, extra_channels=None):
if not isinstance(transitions, Graph):
transitions = Graph.from_edges(transitions)
self.transitions = transitions
self.listeners[c] = set()
+ id = hex(random.randint(0, sys.maxint))[-8:]
+ self._state_transition_pipe_write) = os.pipe()
+ self._state_transition_pipe_write) = (None, None)
+ if self._state_transition_pipe_write is not None:
+ os.write(self._state_transition_pipe_write, "1")
+ # Note: logging here means 1) the initial transition
+ # will not be logged if loggers are set up in the initial
+ # transition! and 2) the final transition will not be logged
+ # if loggers are torn down in the penultimate transition!
+ # This is why, for example, the included loggers are
+ # "always on" rather than listening for start/stop themselves.
self.log('Bus state: %s' % newstate)
return self.publish(newstate, *args, **kwargs)
while self.state not in _states_to_wait_for:
+ if self._state_transition_pipe_read is not None:
+ select.select([self._state_transition_pipe_read], , , interval)
+ os.read(self._state_transition_pipe_read, 1)
+ except (select.error, OSError):
+ # Interrupted due to a signal (being handled by some
+ # other thread). No need to panic, here, just check
+ # the new state and proceed/return.
# From http://psyco.sourceforge.net/psycoguide/bugs.html: