Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List available transactions given the state of the state machine #22

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
1 change: 0 additions & 1 deletion finite_state_machine/draw_state_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def generate_state_diagram_markdown(cls, initial_state):

https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/stateDiagram.html
"""

class_fns = inspect.getmembers(cls, predicate=inspect.isfunction)
state_transitions: List[Transition] = [
func.__fsm for name, func in class_fns if hasattr(func, "__fsm")
Expand Down
34 changes: 33 additions & 1 deletion finite_state_machine/state_machine.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import functools
import types
from typing import NamedTuple, Union
import inspect

from .exceptions import ConditionsNotMet, InvalidStartState

Expand Down Expand Up @@ -45,7 +46,38 @@ def transition(source, target, conditions=None, on_error=None):
raise ValueError("on_error needs to be a bool, int or string")

def transition_decorator(func):
func.__fsm = Transition(func.__name__, source, target, conditions, on_error)
mems = inspect.getmembers(func)
state_machine_instance = [
mem[1]["StateMachine"] for mem in mems if mem[0] == "__globals__"
][0]
Comment on lines +49 to +52
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saving instance of StateMachine to later add "__fsm" attribute. It turned out func was type "function" not "method".

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be replced by state_machine_instance = func.__self__

Copy link
Author

@sammydowds sammydowds Oct 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I tried this it would not work because func is of type function not a method. So I had to dive into the globals referenced for the function type here: https://docs.python.org/3/library/inspect.html. Perhaps I am missing something.

Although, let me play around with it again.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh.... doing a quick google search, doesn't seem like it's possible without inspect. 🤦

I think a possible workaround might be to use a class as a function decorator. Don't worry about it. This works, let's just go with it. If this library has some magic, that's okay. It makes the code which uses this library cleaner which is really what we want.

func.__fsm = Transition(
name=func.__name__,
source=source,
target=target,
conditions=conditions,
on_error=on_error,
)

# creating and/or adding items to __fsm attribute
if hasattr(state_machine_instance, "__fsm"):
if isinstance(source, list):
for src in source:
if src in state_machine_instance.__fsm:
state_machine_instance.__fsm[src].append(target)
else:
state_machine_instance.__fsm[src] = [target]
else:
if source in state_machine_instance.__fsm:
state_machine_instance.__fsm[src].append(target)
else:
state_machine_instance.__fsm[src] = [target]
else:
if isinstance(source, list):
state_machine_instance.__fsm = {}
for src in source:
state_machine_instance.__fsm[src] = [target]
else:
state_machine_instance.__fsm.source = [target]

@functools.wraps(func)
def _wrapper(*args, **kwargs):
Expand Down