fgmacedo / python-statemachine

Python Finite State Machines made easy.
MIT License
854 stars 84 forks source link

chore: Observers rebranded to listeners #448

Closed fgmacedo closed 2 months ago

fgmacedo commented 2 months ago

Observers are now rebranded to {ref}listeners. With expanted support for adding listeners when instantiating a state machine. This allows covering more use cases.

Listeners at class initialization

Listeners are a way to generically add behavior to a state machine without changing its internal implementation.

Example:

>>> from tests.examples.traffic_light_machine import TrafficLightMachine

>>> class LogListener:
...     def __init__(self, name):
...         self.name = name
...
...     def after_transition(self, event, source, target):
...         print(f"{self.name} after: {source.id}--({event})-->{target.id}")
...
...     def on_enter_state(self, target, event):
...         print(f"{self.name} enter: {target.id} from {event}")

>>> sm = TrafficLightMachine(listeners=[LogListener("Paulista Avenue")])
Paulista Avenue enter: green from __initial__

>>> sm.cycle()
Paulista Avenue enter: yellow from cycle
Paulista Avenue after: green--(cycle)-->yellow
'Running cycle from green to yellow'

Adding listeners to an instance

Attach listeners to an already running state machine instance using add_listener.

Exploring our example, imagine that you can implement the LED panel as a listener, that reacts to state changes and turn on/off automatically.

>>> class LedPanel:
...
...     def __init__(self, color: str):
...         self.color = color
...
...     def on_enter_state(self, target: State):
...         if target.id == self.color:
...             print(f"{self.color} turning on")
...
...     def on_exit_state(self, source: State):
...         if source.id == self.color:
...             print(f"{self.color} turning off")

Adding a listener for each traffic light indicator

>>> sm.add_listener(LedPanel("green"), LedPanel("yellow"), LedPanel("red"))  # doctest: +ELLIPSIS
TrafficLightMachine...

Now each "LED panel" reacts to changes in state from the state machine:

>>> sm.cycle()
yellow turning off
Paulista Avenue enter: red from cycle
red turning on
Don't move.
Paulista Avenue after: yellow--(cycle)-->red
'Running cycle from yellow to red'

>>> sm.cycle()
red turning off
Go ahead!
Paulista Avenue enter: green from cycle
green turning on
Paulista Avenue after: red--(cycle)-->green
'Running cycle from red to green'
sonarcloud[bot] commented 2 months ago

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code

See analysis details on SonarCloud

codecov[bot] commented 2 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 100.00%. Comparing base (ee0be69) to head (e16c58f).

Additional details and impacted files ```diff @@ Coverage Diff @@ ## develop #448 +/- ## ========================================= Coverage 100.00% 100.00% ========================================= Files 20 20 Lines 1277 1276 -1 Branches 191 192 +1 ========================================= - Hits 1277 1276 -1 ``` | [Flag](https://app.codecov.io/gh/fgmacedo/python-statemachine/pull/448/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo) | Coverage Δ | | |---|---|---| | [unittests](https://app.codecov.io/gh/fgmacedo/python-statemachine/pull/448/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo) | `100.00% <100.00%> (ø)` | | Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo#carryforward-flags-in-the-pull-request-comment) to find out more.

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.