From 960255b5b1caecb56f744e13d37c3b9351609244 Mon Sep 17 00:00:00 2001 From: Fernando Macedo Date: Thu, 12 Jan 2023 00:14:56 -0300 Subject: [PATCH] bump version 1.0.1 --- docs/auto_examples/all_actions_machine.ipynb | 2 +- .../guess_the_number_machine.ipynb | 2 +- .../auto_examples/order_control_machine.ipynb | 2 +- .../order_control_rich_model_machine.ipynb | 2 +- .../auto_examples/traffic_light_machine.ipynb | 2 +- docs/conf.py | 5 +- docs/releases/1.0.0.md | 228 +---------------- docs/releases/1.0.1.md | 230 ++++++++++++++++++ docs/releases/index.md | 1 + setup.cfg | 2 +- setup.py | 3 +- statemachine/__init__.py | 2 +- 12 files changed, 246 insertions(+), 235 deletions(-) create mode 100644 docs/releases/1.0.1.md diff --git a/docs/auto_examples/all_actions_machine.ipynb b/docs/auto_examples/all_actions_machine.ipynb index a5fd15bd..cf543705 100644 --- a/docs/auto_examples/all_actions_machine.ipynb +++ b/docs/auto_examples/all_actions_machine.ipynb @@ -8,7 +8,7 @@ }, "outputs": [], "source": [ - "%matplotlib inline" + "# import piplite\n# await piplite.install('python-statemachine[diagrams]')" ] }, { diff --git a/docs/auto_examples/guess_the_number_machine.ipynb b/docs/auto_examples/guess_the_number_machine.ipynb index 91e878b1..6d840d3a 100644 --- a/docs/auto_examples/guess_the_number_machine.ipynb +++ b/docs/auto_examples/guess_the_number_machine.ipynb @@ -8,7 +8,7 @@ }, "outputs": [], "source": [ - "%matplotlib inline" + "# import piplite\n# await piplite.install('python-statemachine[diagrams]')" ] }, { diff --git a/docs/auto_examples/order_control_machine.ipynb b/docs/auto_examples/order_control_machine.ipynb index e7a13409..4d83572f 100644 --- a/docs/auto_examples/order_control_machine.ipynb +++ b/docs/auto_examples/order_control_machine.ipynb @@ -8,7 +8,7 @@ }, "outputs": [], "source": [ - "%matplotlib inline" + "# import piplite\n# await piplite.install('python-statemachine[diagrams]')" ] }, { diff --git a/docs/auto_examples/order_control_rich_model_machine.ipynb b/docs/auto_examples/order_control_rich_model_machine.ipynb index 4ab9160e..11fb9ed1 100644 --- a/docs/auto_examples/order_control_rich_model_machine.ipynb +++ b/docs/auto_examples/order_control_rich_model_machine.ipynb @@ -8,7 +8,7 @@ }, "outputs": [], "source": [ - "%matplotlib inline" + "# import piplite\n# await piplite.install('python-statemachine[diagrams]')" ] }, { diff --git a/docs/auto_examples/traffic_light_machine.ipynb b/docs/auto_examples/traffic_light_machine.ipynb index 77fddc60..75843c4f 100644 --- a/docs/auto_examples/traffic_light_machine.ipynb +++ b/docs/auto_examples/traffic_light_machine.ipynb @@ -8,7 +8,7 @@ }, "outputs": [], "source": [ - "%matplotlib inline" + "# import piplite\n# await piplite.install('python-statemachine[diagrams]')" ] }, { diff --git a/docs/conf.py b/docs/conf.py index 842d2cae..915d54ba 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -356,5 +356,8 @@ "min_reported_time": 9999, "thumbnail_size": (400, 280), "image_scrapers": ("matplotlib", MachineScraper()), - # "first_notebook_cell": "", + "first_notebook_cell": ( + "# import piplite\n" + "# await piplite.install('python-statemachine[diagrams]')\n" + ), } diff --git a/docs/releases/1.0.0.md b/docs/releases/1.0.0.md index 0f0e0203..cc9f8f60 100644 --- a/docs/releases/1.0.0.md +++ b/docs/releases/1.0.0.md @@ -2,229 +2,5 @@ *January 11, 2022* -Welcome to StateMachine 1.0! - -This version is a huge refactoring adding a lot of new and exiting features. We hope that -you enjoy. - -These release notes cover the [](#whats-new-in-10), as well as -some [backwards incompatible changes](#backwards-incompatible-changes-in-10) you'll -want to be aware of when upgrading from StateMachine 0.9.0 or earlier. We've -[begun the deprecation process for some features](#deprecated-features-in-10). - - -## Python compatibility in 1.0 - -StateMachine 1.0 supports Python 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, and 3.11. - -This is the last release to support Python 2.7, 3.5 and 3.6. - -## What's new in 1.0 - - -### Validators and Guards - -Transitions now support `cond` and `unless` parameters, to restrict -the execution. - -```python - class ApprovalMachine(StateMachine): - "A workflow machine" - requested = State("Requested", initial=True) - accepted = State("Accepted") - rejected = State("Rejected") - completed = State("Completed") - - validate = requested.to(accepted, cond="is_ok") | requested.to(rejected) -``` - -```{seealso} -See {ref}`validators-and-guards` for more details. -``` - -### Support for diagrams - -You can generate diagrams from your statemachine. - -Example: - -![OrderControl](../images/order_control_machine_initial.png) - - -```{seealso} -See {ref}`diagrams` for more details. -``` - -### Unified dispatch mecanism for callbacks (actions and guards) - -Every single callback, being {ref}`actions` or {ref}`guards`, is now handled equally by the library. - -Also, we've improved the internals in a way that you can implement your callbacks with any -number of arbritrary positional or keyword arguments (`*args, **kwargs`), and the dispatch will -match the available arguments with your method signature. - -This means that if on your `on_enter_()` or `on_execute_()` method, you also -need to know the `source` ({ref}`state`), or the `event` ({ref}`event`), or access a keyword -argument passed with the trigger, you're covered. Just add this parameter to the method and It - will be passed by the dispatch mechanics. - -Example of what's available: - -```py -def action_or_guard_method_name(self, *args, event_data, event, source, state, model, **kwargs): - pass -``` - -```{seealso} -See {ref}`dynamic-dispatch` for more details. -``` - -### Add observers to a running StateMachine - -Observers are a way do generically add behaviour to a StateMachine without -changing it's internal implementation. - -The `StateMachine` itself is registered as an observer, so by using `StateMachine.add_observer()` -an external object can have the same level of functionalities provided to the built-in class. - -```{seealso} -See {ref}`observers` for more details. -``` - -## Minor features in 1.0 - -- Fixed mypy complaining about incorrect type for ``StateMachine`` class. -- The initial {ref}`state` is now entered when the machine starts. The {ref}`actions`, if defined, - `on_enter_state` and `on_enter_` are now called. - - - -## Backwards incompatible changes in 1.0 - - -### Multiple targets from the same origin state - -Prior to this release, as we didn't have {ref}`validators-and-guards`, there wasn't an elegant way -to declare multiples target states starting from the same pair (event, state). But the library -allowed a near-hackish way, by declaring a target state as the result of the `on_` callback. - -So, the previous code (not valid anymore): - -```py -class ApprovalMachine(StateMachine): - "A workflow machine" - requested = State('Requested', initial=True) - accepted = State('Accepted') - rejected = State('Rejected') - - validate = requested.to(accepted, rejected) - - def on_validate(self, current_time): - if self.model.is_ok(): - self.model.accepted_at = current_time - return self.accepted - else: - return self.rejected -``` - -Should be rewriten to use {ref}`guards`, like this: - -``` py -class ApprovalMachine(StateMachine): - "A workflow machine" - requested = State("Requested", initial=True) - accepted = State("Accepted") - rejected = State("Rejected") - - validate = requested.to(accepted, conditions="is_ok") | requested.to(rejected) - - def on_validate(self, current_time): - self.model.accepted_at = current_time -``` - -```{seealso} -See {ref}`validators-and-guards` of more details. -``` - -### StateMachine now enters the initial state - -This issue was reported at [#265](https://github.com/fgmacedo/python-statemachine/issues/265). - -Now StateMachine will execute the actions associated with the `on_enter_state` and -`on_enter_` when initialized, if they exists. - -```{seealso} -See {ref}`State actions` for more details. -``` - -### Integrity is checked at class definition - -Statemachine integrity checks are now performed at class declaration (import time) instead of on -instance creation. This allows early feedback of invalid definitions. - -This was the previous behaviour, you only got an error when trying to instantiate a StateMachine: - -```py -class CampaignMachine(StateMachine): - "A workflow machine" - draft = State('Draft', initial=True) - producing = State('Being produced') - closed = State('Closed', initial=True) # Should raise an Exception when instantiated - - add_job = draft.to(draft) | producing.to(producing) - produce = draft.to(producing) - deliver = producing.to(closed) - -with pytest.raises(exceptions.InvalidDefinition): - CampaignMachine() -``` - -Not this is performed as the class definition is performed: - -```py -with pytest.raises(exceptions.InvalidDefinition): - - class CampaignMachine(StateMachine): - "A workflow machine" - draft = State("Draft", initial=True) - producing = State("Being produced") - closed = State( - "Closed", initial=True - ) # Should raise an Exception right after the class is defined - - add_job = draft.to(draft) | producing.to(producing) - produce = draft.to(producing) - deliver = producing.to(closed) -``` - -### Other backwards incompatible changes in 1.0 - -- Due to the check validations and setup performed at the machine initialization, it's now harder - to perform monkey-patching to add callbacks at runtime (not a bad thing after all). -- `TransitionNotAllowed` changed internal attr from `transition` to `event`. -- `CombinedTransition` does not exist anymore. {ref}`State` now holds a flat {ref}`Transition` list - called `TransitionList` that implements de `OR` operator. This turns a valid StateMachine - traversal much easier: `[transition for state in machine.states for transition in state.transitions]`. -- `StateMachine.get_transition` is removed. See {ref}`event`. -- The previous excetions `MultipleStatesFound` and `MultipleTransitionCallbacksFound` are removed. - Since now you can have more than one callback defined to the same transition. -- `on_enter_state` and `on_exit_state` now accepts any combination of parameters following the - {ref}`dynamic-dispatch` rules. Previously it only accepted the `state` param. -- `Transition.__init__` param `on_execute` renamed to simply `on`, and now follows the -{ref}`dynamic-dispatch`. -- `Transition.destinations` removed in favor of `Transition.target` (following SCXML convention). -Now each transition only points to a unique target. Each `source->target` pair is holded by a -single `Transition`. - -## Deprecated features in 1.0 - -### Statemachine class - -- `StateMachine.run` is deprecated in favor of `StateMachine.send`. -- `StateMachine.allowed_transitions` is deprecated in favor of `StateMachine.allowed_events`. -- `Statemachine.is_` is deprecated in favor of `StateMachine..is_active`. - - -### State class - -- `State.identification` is deprecated in favor of `State.id`. +This release tag was replaced by [](1.0.1.md) due to an error on the metadata when uploading to +pypi. diff --git a/docs/releases/1.0.1.md b/docs/releases/1.0.1.md new file mode 100644 index 00000000..336b57ce --- /dev/null +++ b/docs/releases/1.0.1.md @@ -0,0 +1,230 @@ +# StateMachine 1.0.1 + +*January 11, 2022* + +Welcome to StateMachine 1.0.1! + +This version is a huge refactoring adding a lot of new and exiting features. We hope that +you enjoy. + +These release notes cover the [](#whats-new-in-10), as well as +some [backwards incompatible changes](#backwards-incompatible-changes-in-10) you'll +want to be aware of when upgrading from StateMachine 0.9.0 or earlier. We've +[begun the deprecation process for some features](#deprecated-features-in-10). + + +## Python compatibility in 1.0 + +StateMachine 1.0 supports Python 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, and 3.11. + +This is the last release to support Python 2.7, 3.5 and 3.6. + +## What's new in 1.0 + + +### Validators and Guards + +Transitions now support `cond` and `unless` parameters, to restrict +the execution. + +```python + class ApprovalMachine(StateMachine): + "A workflow machine" + requested = State("Requested", initial=True) + accepted = State("Accepted") + rejected = State("Rejected") + completed = State("Completed") + + validate = requested.to(accepted, cond="is_ok") | requested.to(rejected) +``` + +```{seealso} +See {ref}`validators-and-guards` for more details. +``` + +### Support for diagrams + +You can generate diagrams from your statemachine. + +Example: + +![OrderControl](../images/order_control_machine_initial.png) + + +```{seealso} +See {ref}`diagrams` for more details. +``` + +### Unified dispatch mecanism for callbacks (actions and guards) + +Every single callback, being {ref}`actions` or {ref}`guards`, is now handled equally by the library. + +Also, we've improved the internals in a way that you can implement your callbacks with any +number of arbritrary positional or keyword arguments (`*args, **kwargs`), and the dispatch will +match the available arguments with your method signature. + +This means that if on your `on_enter_()` or `on_execute_()` method, you also +need to know the `source` ({ref}`state`), or the `event` ({ref}`event`), or access a keyword +argument passed with the trigger, you're covered. Just add this parameter to the method and It + will be passed by the dispatch mechanics. + +Example of what's available: + +```py +def action_or_guard_method_name(self, *args, event_data, event, source, state, model, **kwargs): + pass +``` + +```{seealso} +See {ref}`dynamic-dispatch` for more details. +``` + +### Add observers to a running StateMachine + +Observers are a way do generically add behaviour to a StateMachine without +changing it's internal implementation. + +The `StateMachine` itself is registered as an observer, so by using `StateMachine.add_observer()` +an external object can have the same level of functionalities provided to the built-in class. + +```{seealso} +See {ref}`observers` for more details. +``` + +## Minor features in 1.0 + +- Fixed mypy complaining about incorrect type for ``StateMachine`` class. +- The initial {ref}`state` is now entered when the machine starts. The {ref}`actions`, if defined, + `on_enter_state` and `on_enter_` are now called. + + + +## Backwards incompatible changes in 1.0 + + +### Multiple targets from the same origin state + +Prior to this release, as we didn't have {ref}`validators-and-guards`, there wasn't an elegant way +to declare multiples target states starting from the same pair (event, state). But the library +allowed a near-hackish way, by declaring a target state as the result of the `on_` callback. + +So, the previous code (not valid anymore): + +```py +class ApprovalMachine(StateMachine): + "A workflow machine" + requested = State('Requested', initial=True) + accepted = State('Accepted') + rejected = State('Rejected') + + validate = requested.to(accepted, rejected) + + def on_validate(self, current_time): + if self.model.is_ok(): + self.model.accepted_at = current_time + return self.accepted + else: + return self.rejected +``` + +Should be rewriten to use {ref}`guards`, like this: + +``` py +class ApprovalMachine(StateMachine): + "A workflow machine" + requested = State("Requested", initial=True) + accepted = State("Accepted") + rejected = State("Rejected") + + validate = requested.to(accepted, conditions="is_ok") | requested.to(rejected) + + def on_validate(self, current_time): + self.model.accepted_at = current_time +``` + +```{seealso} +See {ref}`validators-and-guards` of more details. +``` + +### StateMachine now enters the initial state + +This issue was reported at [#265](https://github.com/fgmacedo/python-statemachine/issues/265). + +Now StateMachine will execute the actions associated with the `on_enter_state` and +`on_enter_` when initialized, if they exists. + +```{seealso} +See {ref}`State actions` for more details. +``` + +### Integrity is checked at class definition + +Statemachine integrity checks are now performed at class declaration (import time) instead of on +instance creation. This allows early feedback of invalid definitions. + +This was the previous behaviour, you only got an error when trying to instantiate a StateMachine: + +```py +class CampaignMachine(StateMachine): + "A workflow machine" + draft = State('Draft', initial=True) + producing = State('Being produced') + closed = State('Closed', initial=True) # Should raise an Exception when instantiated + + add_job = draft.to(draft) | producing.to(producing) + produce = draft.to(producing) + deliver = producing.to(closed) + +with pytest.raises(exceptions.InvalidDefinition): + CampaignMachine() +``` + +Not this is performed as the class definition is performed: + +```py +with pytest.raises(exceptions.InvalidDefinition): + + class CampaignMachine(StateMachine): + "A workflow machine" + draft = State("Draft", initial=True) + producing = State("Being produced") + closed = State( + "Closed", initial=True + ) # Should raise an Exception right after the class is defined + + add_job = draft.to(draft) | producing.to(producing) + produce = draft.to(producing) + deliver = producing.to(closed) +``` + +### Other backwards incompatible changes in 1.0 + +- Due to the check validations and setup performed at the machine initialization, it's now harder + to perform monkey-patching to add callbacks at runtime (not a bad thing after all). +- `TransitionNotAllowed` changed internal attr from `transition` to `event`. +- `CombinedTransition` does not exist anymore. {ref}`State` now holds a flat {ref}`Transition` list + called `TransitionList` that implements de `OR` operator. This turns a valid StateMachine + traversal much easier: `[transition for state in machine.states for transition in state.transitions]`. +- `StateMachine.get_transition` is removed. See {ref}`event`. +- The previous excetions `MultipleStatesFound` and `MultipleTransitionCallbacksFound` are removed. + Since now you can have more than one callback defined to the same transition. +- `on_enter_state` and `on_exit_state` now accepts any combination of parameters following the + {ref}`dynamic-dispatch` rules. Previously it only accepted the `state` param. +- `Transition.__init__` param `on_execute` renamed to simply `on`, and now follows the +{ref}`dynamic-dispatch`. +- `Transition.destinations` removed in favor of `Transition.target` (following SCXML convention). +Now each transition only points to a unique target. Each `source->target` pair is holded by a +single `Transition`. + +## Deprecated features in 1.0 + +### Statemachine class + +- `StateMachine.run` is deprecated in favor of `StateMachine.send`. +- `StateMachine.allowed_transitions` is deprecated in favor of `StateMachine.allowed_events`. +- `Statemachine.is_` is deprecated in favor of `StateMachine..is_active`. + + +### State class + +- `State.identification` is deprecated in favor of `State.id`. diff --git a/docs/releases/index.md b/docs/releases/index.md index 40dc3e2a..8d90a0a5 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -15,6 +15,7 @@ Below are release notes through StateMachine and its patch releases. ```{toctree} :maxdepth: 1 +1.0.1 1.0.0 ``` diff --git a/setup.cfg b/setup.cfg index 8ad316cd..f1b416a9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.0.0 +current_version = 1.0.1 commit = True tag = True diff --git a/setup.py b/setup.py index 32991002..d3f2c918 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name="python-statemachine", - version="1.0.0", + version="1.0.1", description="Python Finite State Machines made easy.", long_description=long_description, author="Fernando Macedo", @@ -23,6 +23,7 @@ url="https://github.com/fgmacedo/python-statemachine", packages=[ "statemachine", + "statemachine.contrib", ], package_dir={"statemachine": "statemachine"}, extras_require=extras_require, diff --git a/statemachine/__init__.py b/statemachine/__init__.py index 31853450..5b5d08b0 100644 --- a/statemachine/__init__.py +++ b/statemachine/__init__.py @@ -7,6 +7,6 @@ __author__ = """Fernando Macedo""" __email__ = "fgmacedo@gmail.com" -__version__ = "1.0.0" +__version__ = "1.0.1" __all__ = ["StateMachine", "State"]