shellbot.machines.base module¶
-
class
shellbot.machines.base.
Machine
(bot=None, states=None, transitions=None, initial=None, during=None, on_enter=None, on_exit=None, **kwargs)[source]¶ Bases:
object
Implements a state machine
The life cycle of a machine can be described as follows:
A machine instance is created and configured:
a_bot = ShellBot(...) machine = Machine(bot=a_bot) machine.set(states=states, transitions=transitions, ...
The machine is switched on and ticked at regular intervals:
machine.start()
Machine can process more events than ticks:
machine.execute('hello world')
When a machine is expecting data from the chat space, it listens from the
fan
queue used by the shell:engine.fan.put('special command')
When the machine is coming end of life, resources can be disposed:
machine.stop()
credit: Alex Bertsch <abertsch@dropbox.com> securitybot/state_machine.py
-
DEFER_DURATION
= 0.0¶
-
TICK_DURATION
= 0.2¶
-
build
(states, transitions, initial, during=None, on_enter=None, on_exit=None)[source]¶ Builds a complete state machine
Parameters: - states (list of str) – All states supported by this machine
- transitions (list of dict) –
Transitions between states. Each transition is a dictionary. Each dictionary must feature following keys:
source (str): The source state of the transition target (str): The target state of the transition- Each dictionary may contain following keys:
- condition (function): A condition that must be true for the
- transition to occur. If no condition is provided then the state machine will transition on a step.
- action (function): A function to be executed while the
- transition occurs.
- initial (str) – The initial state
- during (dict) – A mapping of states to functions to execute while in that state. Each key should map to a callable function.
- on_enter (dict) – A mapping of states to functions to execute when entering that state. Each key should map to a callable function.
- on_exit (dict) – A mapping of states to functions to execute when exiting that state. Each key should map to a callable function.
-
current_state
¶ Provides current state
Returns: State This function raises AttributeError if it is called before
build()
.
-
execute
(arguments=None, **kwargs)[source]¶ Processes data received from the chat
Parameters: arguments (str is recommended) – input to be injected into the state machine This function can be used to feed the machine asynchronously
-
get
(key, default=None)[source]¶ Retrieves the value of one key
Parameters: - key (str) – one attribute of this state machine instance
- default (an type that can be serialized) – default value is the attribute has not been set yet
This function can be used across multiple processes, so that a consistent view of the state machine is provided.
-
is_running
¶ Determines if this machine is runnning
Returns: True or False
-
on_init
(**kwargs)[source]¶ Adds to machine initialisation
This function should be expanded in sub-class, where necessary.
Example:
def on_init(self, prefix='my.machine', **kwargs): ...
-
on_reset
()[source]¶ Adds processing to machine reset
This function should be expanded in sub-class, where necessary.
Example:
def on_reset(self): self.sub_machine.reset()
-
on_start
()[source]¶ Adds to machine start
This function is invoked when the machine is started or restarted. It can be expanded in sub-classes where required.
Example:
def on_start(self): # clear bot store on machine start self.bot.forget()
-
on_stop
()[source]¶ Adds to machine stop
This function is invoked when the machine is stopped. It can be expanded in sub-classes where required.
Example:
def on_stop(self): # dump bot store on machine stop self.bot.publisher.put( self.bot.id, self.bot.recall('input'))
-
reset
()[source]¶ Resets a state machine before it is restarted
Returns: True if the machine has been actually reset, else False This function moves a state machine back to its initial state. A typical use case is when you have to recycle a state machine multiple times, like in the following example:
if new_cycle(): machine.reset() machine.start()
If the machine is running, calling
reset()
will have no effect and you will get False in return. Therefore, if you have to force a reset, you may have to stop the machine first.Example of forced reset:
machine.stop() machine.reset()
-
restart
(**kwargs)[source]¶ Restarts the machine
This function is very similar to reset(), except that it also starts the machine on successful reset. Parameters given to it are those that are expected by start().
Note: this function has no effect on a running machine.
-
run
()[source]¶ Continuously ticks the machine
This function is looping in the background, and calls
step(event='tick')
at regular intervals.The recommended way for stopping the process is to call the function
stop()
. For example:machine.stop()
The loop is also stopped when the parameter
general.switch
is changed in the context. For example:engine.set('general.switch', 'off')
-
set
(key, value)[source]¶ Remembers the value of one key
Parameters: - key (str) – one attribute of this state machine instance
- value (an type that can be serialized) – new value of the attribute
This function can be used across multiple processes, so that a consistent view of the state machine is provided.
-
start
(tick=None, defer=None)[source]¶ Starts the machine
Parameters: - tick (positive number) – The duration set for each tick (optional)
- defer (positive number) – wait some seconds before the actual work (optional)
Returns: either the process that has been started, or None
This function starts a separate thread to tick the machine in the background.
-
state
(name)[source]¶ Provides a state by name
Parameters: name (str) – The label of the target state Returns: State This function raises KeyError if an unknown name is provided.
-
step
(**kwargs)[source]¶ Brings some life to the state machine
Thanks to
**kwargs
, it is easy to transmit parameters to underlying functions: -current_state.during(**kwargs)
-transition.condition(**kwargs)
Since parameters can vary on complex state machines, you are advised to pay specific attention to the signatures of related functions. If you expect some parameter in a function, use ``kwargs.get()``to get its value safely.
For example, to inject the value of a gauge in the state machine on each tick:
def remember(**kwargs): gauge = kwargs.get('gauge') if gauge: db.save(gauge) during = { 'measuring', remember } ... machine.build(during=during, ... ) while machine.is_running: machine.step(gauge=get_measurement())
Or, if you have to transition on a specific threshold for a gauge, you could do:
def if_threshold(**kwargs): gauge = kwargs.get('gauge') if gauge > 20: return True return False def raise_alarm(): mail.post_message() transitions = [ {'source': 'normal', 'target': 'alarm', 'condition': if_threshold, 'action': raise_alarm}, ... ] ... machine.build(transitions=transitions, ... ) while machine.is_running: machine.step(gauge=get_measurement())
Shellbot is using this mechanism for itself, and the function can be called at various occasions: - machine tick - This is done at regular intervals in time - input from the chat - Typically, in response to a question - inbound message - Received from subscription, over the network
Following parameters are used for machine ticks: - event=’tick’ - fixed value
Following parameters are used for chat input: - event=’input’ - fixed value - arguments - the text that is submitted from the chat
Following parameters are used for subscriptions: - event=’inbound’ - fixed value - message - the object that has been transmitted
This machine should report on progress by sending messages with one or multiple
self.bot.say("Whatever message")
.
-
class
shellbot.machines.base.
State
(name, during=None, on_enter=None, on_exit=None)[source]¶ Bases:
object
Represents a state of the machine
Each state has a function to perform while it’s active, when it’s entered into, and when it’s exited. These functions may be None.
-
class
shellbot.machines.base.
Transition
(source, target, condition=None, action=None)[source]¶ Bases:
object
Represents a transition between two states
Each transition object holds a reference to its source and destination states, as well as the condition function it requires for transitioning and the action to perform upon transitioning.