Skip to content

Extended the documentation to show a few more#340

Merged
fgmacedo merged 2 commits intofgmacedo:developfrom
Rosi2143:extend_documentation
Feb 10, 2023
Merged

Extended the documentation to show a few more#340
fgmacedo merged 2 commits intofgmacedo:developfrom
Rosi2143:extend_documentation

Conversation

@Rosi2143
Copy link
Copy Markdown
Contributor

usage aéxamples:

  • list of conditions
  • how to use validators
  • possible usage of properties

Signed-off-by: Schrotti Schrott.Micha@web.de

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 28, 2023

Codecov Report

Base: 100.00% // Head: 100.00% // No change to project coverage 👍

Coverage data is based on head (0274159) compared to base (cb28828).
Patch has no changes to coverable lines.

Additional details and impacted files
@@            Coverage Diff            @@
##           develop      #340   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           18        18           
  Lines          862       862           
  Branches       120       140   +20     
=========================================
  Hits           862       862           
Flag Coverage Δ
unittests 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
statemachine/state.py 100.00% <ø> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment thread docs/guards.md
Comment on lines +39 to +40
* Single condition: `cond="condition"`
* Multiple conditions: `cond=["condition1", "condition2"]`
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

praise: Nice way to explain! Thanks!

Comment thread docs/guards.md
Comment on lines +75 to +94
unpaid.to(failed, validators="validator", unless="paused") |
failed.to(paid, cond=["payment_success", "offer_valid"])
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

praise: Nice that you demonstrated that properties and attributes can be used directly.

suggestion: Maybe we could add an explicit note at dynamic dispatch that this is possible.

Comment thread statemachine/state.py Outdated
But the good thing is that it implements the ``OR`` operator to combine transitions,
so you can use the ``|`` syntax to compound a list of transitions and assign
to the same event.
You have to declare all transitions for a state in one single line.
Copy link
Copy Markdown
Owner

@fgmacedo fgmacedo Jan 28, 2023

Choose a reason for hiding this comment

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

You have to declare all transitions for a state in one single line.

question: Why do you have added this statement? Can you give me an example?

I think that there's not that blocks someone from writing on multiple lines.

Example

Consider the Guess the number example, someone can write the events like this and it also works:

class GuessTheNumberMachine(StateMachine):

    start = State("Start", initial=True)
    low = State("Low")
    high = State("High")
    won = State("Won", final=True)
    lose = State("Lose", final=True)

    guess = lose.from_(low, high, cond="max_guesses_reached")
    guess = guess | won.from_(low, high, cond="guess_is_equal")
    guess = guess | low.from_(low, high, start, cond="guess_is_lower")
    guess = guess | high.from_(low, high, start, cond="guess_is_higher")    

Or even the compact form:

class GuessTheNumberMachine(StateMachine):

    start = State("Start", initial=True)
    low = State("Low")
    high = State("High")
    won = State("Won", final=True)
    lose = State("Lose", final=True)

    guess = lose.from_(low, high, cond="max_guesses_reached")
    guess |= won.from_(low, high, cond="guess_is_equal")
    guess |= low.from_(low, high, start, cond="guess_is_lower")
    guess |= high.from_(low, high, start, cond="guess_is_higher")
    

I personally prefer the "one-liner", but I would not say "have to".

What do you think?

@fgmacedo
Copy link
Copy Markdown
Owner

fgmacedo commented Feb 8, 2023

Hi @Rosi2143, how are you?

Are you still working on this? It's near the point that it can be merged. Please let me know if you need any help.

Best regards!

@Rosi2143
Copy link
Copy Markdown
Contributor Author

Rosi2143 commented Feb 8, 2023

Hi @fgmacedo ,

I send a reply directly from Thunderbird that somehow didn't make it into this converstation. It's a test that shows - imho - that multiple lines produce a wrong result.

Here the copy:

Hi,

here is my example:

"""Example of python module statemachine: https://pypi.org/project/python-statemachine/"""

import sys
import os
import logging
import inspect

from statemachine import State
from statemachine import StateMachine


class InvoiceStateMachine(StateMachine):
    unpaid = State("unpaid", initial=True)
    paid = State("paid")
    failed = State("failed")
    error = State("error")

    paused = False
    offer_valid = True

    pay = (
        unpaid.to(paid, cond="payment_success") |
        unpaid.to(paid, cond=["payment_success", "offer_valid"])
    )
    pay = unpaid.to(failed, unless="paused")
    pay = unpaid.to(error)

    def payment_success(self):
        return True


sm = InvoiceStateMachine()
print(sm.current_state.name)

sm.send("pay")
print(sm.current_state.name)

I get:
python test.py
unpaid
error

If I start removing lines - from the bottom the state keeps changing - always resulting in the last possible transition:

"""Example of python module statemachine: https://pypi.org/project/python-statemachine/"""

import sys
import os
import logging
import inspect

from statemachine import State
from statemachine import StateMachine


class InvoiceStateMachine(StateMachine):
    unpaid = State("unpaid", initial=True)
    paid = State("paid")
    failed = State("failed")
#    error = State("error")

    paused = False
    offer_valid = True

    pay = (
        unpaid.to(paid, cond="payment_success") |
        unpaid.to(paid, cond=["payment_success", "offer_valid"])
    )
    pay = unpaid.to(failed, unless="paused")
#    pay = unpaid.to(error)

    def payment_success(self):
        return True


sm = InvoiceStateMachine()
print(sm.current_state.name)

sm.send("pay")
print(sm.current_state.name)

python test.py
unpaid
failed

"""Example of python module statemachine: https://pypi.org/project/python-statemachine/"""

import sys
import os
import logging
import inspect

from statemachine import State
from statemachine import StateMachine


class InvoiceStateMachine(StateMachine):
    unpaid = State("unpaid", initial=True)
    paid = State("paid")
#    failed = State("failed")
#    error = State("error")

    paused = False
    offer_valid = True

    pay = (
        unpaid.to(paid, cond="payment_success") |
        unpaid.to(paid, cond=["payment_success", "offer_valid"])
    )
#    pay = unpaid.to(failed, unless="paused")
#    pay = unpaid.to(error)

    def payment_success(self):
        return True


sm = InvoiceStateMachine()
print(sm.current_state.name)

sm.send("pay")
print(sm.current_state.name)

python test.py
unpaid
paid

If I put it all in a single line I get the expected result:

"""Example of python module statemachine: https://pypi.org/project/python-statemachine/"""

import sys
import os
import logging
import inspect

from statemachine import State
from statemachine import StateMachine


class InvoiceStateMachine(StateMachine):
    unpaid = State("unpaid", initial=True)
    paid = State("paid")
    failed = State("failed")
    error = State("error")

    paused = False
    offer_valid = True

    pay = (
        unpaid.to(paid, cond="payment_success") |
        unpaid.to(paid, cond=["payment_success", "offer_valid"]) |
        unpaid.to(failed, unless="paused") |
        unpaid.to(error)
	)

    def payment_success(self):
        return True


sm = InvoiceStateMachine()
print(sm.current_state.name)

sm.send("pay")
print(sm.current_state.name)

python test.py
unpaid
paid

@fgmacedo
Copy link
Copy Markdown
Owner

fgmacedo commented Feb 8, 2023

Ah, ok, now I see.

The difference is that we should think in terms of the assignment of a variable.

With this construction, after the first assignment of pay = ..., we literally get the previous one out of scope, and its previous value is lost:

    pay = (
        unpaid.to(paid, cond="payment_success") |
        unpaid.to(paid, cond=["payment_success", "offer_valid"])
    )
    pay = unpaid.to(failed, unless="paused")
    pay = unpaid.to(error)

But this is just regular Python code working as expected, so there's nothing special happening here. Every time you assign a new value to an existing variable, you get the old value out of scope. This is why I think that we don't need to be explicit on the docs.

When I say "assign transitions to an event name using multiple lines", I automatically think that for this to work, I need to concatenate to the previous value.

This can be done using the previous value:

    pay = (
        unpaid.to(paid, cond="payment_success") |
        unpaid.to(paid, cond=["payment_success", "offer_valid"])
    )
    pay = pay | unpaid.to(failed, unless="paused")
    pay = pay | unpaid.to(error)

Or using the "in-place OR" operator:

    pay = (
        unpaid.to(paid, cond="payment_success") |
        unpaid.to(paid, cond=["payment_success", "offer_valid"])
    )
    pay |= unpaid.to(failed, unless="paused")
    pay |= unpaid.to(error)

But I think that this doesn't need to be mentioned in the docs as It's more related to "how Python works" than "how to use the library". What do you think?

@Rosi2143
Copy link
Copy Markdown
Contributor Author

Rosi2143 commented Feb 9, 2023

Hi @fgmacedo ,

I'll think of something to rewrite the lines and push a new change.

usage aéxamples:

- list of conditions
- how to use validators
- possible usage of properties

Signed-off-by: Schrotti <Schrott.Micha@web.de>
corrected the description of how to add transitions
using multiple lines

Signed-off-by: Schrotti <Schrott.Micha@web.de>
@Rosi2143 Rosi2143 force-pushed the extend_documentation branch from cd0560b to 0274159 Compare February 10, 2023 15:50
@sonarqubecloud
Copy link
Copy Markdown

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

Comment thread statemachine/state.py

>>> transitions = draft.to(draft) | producing.to(closed)

... and you can append additional transitions for a state to previous definitions.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

praise: Nice!

Copy link
Copy Markdown
Owner

@fgmacedo fgmacedo left a comment

Choose a reason for hiding this comment

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

Nice! Thank you!

@fgmacedo fgmacedo merged commit 26f3096 into fgmacedo:develop Feb 10, 2023
@Rosi2143 Rosi2143 deleted the extend_documentation branch February 10, 2023 22:12
@fgmacedo
Copy link
Copy Markdown
Owner

fgmacedo commented Mar 3, 2023

Hi @Rosi2143 , how are you?

Please consider adding yourself to the contributor's list: https://github.com/fgmacedo/python-statemachine/blob/develop/docs/authors.md

Best!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants