Replies: 3 comments 11 replies
-
Can you give more context on what you're trying to solve by using a dict to store states? Is your SM definition dynamic? A helper that you can use is the from statemachine import StateMachine, State
from statemachine.states import States
class SM(StateMachine):
states = States({
name: State(initial=idx == 0) for idx, name in enumerate(["initial", "final"])
})
finish = states.initial.to(states.final) |
Beta Was this translation helpful? Give feedback.
-
@fgmacedo Yes that's what i am trying to do, make the definition dynamic on a specific input. So far i managed to do it by setting an environment variable. The page you sent is very helpful, but i can't do it through an intialisation function, which means the states would have to be specified in the environment/variable somehow. Do you have any suggestions? Debugging it i found it was becauses the states are created on importing the class and not its intialisation. |
Beta Was this translation helpful? Give feedback.
-
The factory.StateMachineMetaclass plays a crucial role in generating StateMachine classes from a static code definition. Thanks to Python's dynamic capabilities, each metaclass, including the fundamental one, type, has the ability to create classes. Consequently, in Python, a class is essentially an instance of a metaclass. To illustrate this concept, I have prepared a simple example for you. This example demonstrates how to craft a "dynamic factory" leveraging the metaclass to produce a new StateMachine class, based on a dictionary detailing the states and transitions of the state machine. Although this example does not comprehensively address all parameters for creating States and Transitions, it serves as an excellent starting point. Should this approach resonate well with the community, we might consider integrating such a factory into the library itself. Please upvote the answer if you find this to be useful. Dynamic created machineA demonstration of how to create a :ref: from statemachine.factory import StateMachineMetaclass
from statemachine.state import State
from statemachine.statemachine import StateMachine
def create_machine_class_from_definition(name: str, definition: dict):
"""
Creates a StateMachine class from a dictionary definition, using the StateMachineMetaclass metaclass.
It maps the definition to the StateMachineMetaclass parameters and then creates the class.
Example usage to generate a traffic light machine:
>>> machine = create_machine_class_from_definition(
... "TrafficLightMachine",
... {
... "states": {
... "green": {"initial": True},
... "yellow": {},
... "red": {},
... },
... "events": {
... "change": [
... {"from": "green", "to": "yellow"},
... {"from": "yellow", "to": "red"},
... {"from": "red", "to": "green"},
... ]
... },
... }
... )
"""
states_instances = {
state_id: State(**state_kwargs)
for state_id, state_kwargs in definition["states"].items()
}
events = {}
for event_name, transitions in definition["events"].items():
for transition_data in transitions:
source = states_instances[transition_data["from"]]
target = states_instances[transition_data["to"]]
transition = source.to(target, event=event_name)
if event_name in events:
events[event_name] |= transition
else:
events[event_name] = transition
attrs_mapper = {**states_instances, **events}
return StateMachineMetaclass(name, (StateMachine,), attrs_mapper) Putting It all togetherCampaignMachine = create_machine_class_from_definition(
"CampaignMachine",
{
"states": {
"draft": {"initial": True, "final": False},
"producing": {"initial": False, "final": False},
"closed": {"initial": False, "final": True},
},
"events": {
"add_job": [
{"from": "draft", "to": "draft"},
{"from": "producing", "to": "producing"},
],
"produce": [{"from": "draft", "to": "producing"}],
"deliver": [{"from": "producing", "to": "closed"}],
},
}
) Asserting campaign machine declarationassert CampaignMachine.draft.initial
assert not CampaignMachine.draft.final
assert not CampaignMachine.producing.initial
assert not CampaignMachine.producing.final
assert not CampaignMachine.closed.initial
assert CampaignMachine.closed.final Testing our campaign machinesm = CampaignMachine()
res = sm.send("produce")
assert sm.draft.is_active is False
assert sm.producing.is_active is True
assert sm.closed.is_active is False
assert sm.current_state == sm.producing
assert sm.current_state_value == "producing" |
Beta Was this translation helpful? Give feedback.
-
Currently i am trying to declare states in a map like
states_storage['Start'] = State('Start', initial=True)
but this doesn't actually make the states, instead in the factory.py file it says there are no states, instead when i doStart = State('Start', initial=True)
it works as intended. Is there a way to get working with dicts? The example given is simplified, the actual program is a bit more complexBeta Was this translation helpful? Give feedback.
All reactions