Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-transitions for openSUSE:Factory checked in at 2021-10-26 20:13:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-transitions (Old) and /work/SRC/openSUSE:Factory/.python-transitions.new.1890 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-transitions" Tue Oct 26 20:13:43 2021 rev:10 rq:927350 version:0.8.10 Changes: -------- --- /work/SRC/openSUSE:Factory/python-transitions/python-transitions.changes 2021-10-16 22:47:55.616706415 +0200 +++ /work/SRC/openSUSE:Factory/.python-transitions.new.1890/python-transitions.changes 2021-10-26 20:14:23.586031972 +0200 @@ -1,0 +2,21 @@ +Sun Oct 24 17:34:22 UTC 2021 - Ben Greiner <c...@bnavigator.de> + +- Update to version 0.8.10 + * Feature #545: The literal 'self' (default model parameter of + Machine) has been replaced by the class variable + Machine.self_literal = 'self'. Machine now performs an identity + check (instead of a value check) with mod is self.self_literal + to determine whether it should act as a model. While 'self' + should still work when passed to the model parameter, we + encourage using Machine.self_literal from now on. This was done + to enable easier override of Machine.__eq__ in subclasses + (thanks @VKSolovev). + * Bug #547: Introduce HierarchicalMachine.prefix_path to resolve + global state names since the HSM stack is not reliable when + queued=True (thanks @jankrejci). + * Bug #548: HSM source states were exited even though they are + parents of the destination state (thanks @wes-public-apps). +- Don't test optional extra graphviz on python36: pygraphviz + dropped Python 3.6 support + +------------------------------------------------------------------- Old: ---- transitions-0.8.9.tar.gz New: ---- transitions-0.8.10.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-transitions.spec ++++++ --- /var/tmp/diff_new_pack.ddMeHh/_old 2021-10-26 20:14:24.378032390 +0200 +++ /var/tmp/diff_new_pack.ddMeHh/_new 2021-10-26 20:14:24.382032392 +0200 @@ -18,8 +18,9 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} +%bcond_without python2 Name: python-transitions -Version: 0.8.9 +Version: 0.8.10 Release: 0 Summary: A lightweight, object-oriented Python state machine implementation License: MIT @@ -35,12 +36,15 @@ BuildArch: noarch # SECTION test requirements BuildRequires: %{python_module dill} -BuildRequires: %{python_module graphviz} -BuildRequires: %{python_module mock} BuildRequires: %{python_module pycodestyle} -BuildRequires: %{python_module pygraphviz} BuildRequires: %{python_module pytest} BuildRequires: %{python_module six} +# pygraphviz dropped support for Python 3.6 +BuildRequires: %{python_module graphviz if (%python-base without python36-base)} +BuildRequires: %{python_module pygraphviz if (%python-base without python36-base)} +%if %{with python2} +BuildRequires: python2-mock +%endif # png support for graphviz BuildRequires: graphviz-gnome # /SECTION @@ -63,12 +67,12 @@ %python_expand %fdupes %{buildroot}%{$python_sitelib} %check -rm -v tests/test_codestyle.py -%pytest +%pytest --ignore tests/test_codestyle.py %files %{python_files} %license LICENSE %doc Changelog.md README.md -%{python_sitelib}/* +%{python_sitelib}/transitions +%{python_sitelib}/transitions-%{version}*-info %changelog ++++++ transitions-0.8.9.tar.gz -> transitions-0.8.10.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/Changelog.md new/transitions-0.8.10/Changelog.md --- old/transitions-0.8.9/Changelog.md 2021-09-02 12:39:49.000000000 +0200 +++ new/transitions-0.8.10/Changelog.md 2021-10-04 17:19:22.000000000 +0200 @@ -1,8 +1,16 @@ # Changelog +## 0.8.10 (October 2021) + +Release 0.8.10 is a minor release and contains two bug fixes for the HSM extension and changes how the 'self' literal string is handled. + +- Feature #545: The literal 'self' (default model parameter of `Machine`) has been replaced by the class variable `Machine.self_literal = 'self'`. `Machine` now performs an identity check (instead of a value check) with `mod is self.self_literal` to determine whether it should act as a model. While 'self' should still work when passed to the `model` parameter, we encourage using `Machine.self_literal` from now on. This was done to enable easier override of `Machine.__eq__` in subclasses (thanks @VKSolovev). +- Bug #547: Introduce `HierarchicalMachine.prefix_path` to resolve global state names since the HSM stack is not reliable when `queued=True` (thanks @jankrejci). +- Bug #548: `HSM` source states were exited even though they are parents of the destination state (thanks @wes-public-apps). + ## 0.8.9 (September 2021) -Release 0.8.8 is a minor release and contains a bugfix for HSM, a feature for `GraphSupport` and changes internal cache handling: +Release 0.8.9 is a minor release and contains a bugfix for HSM, a feature for `GraphSupport` and changes internal cache handling: - Bugfix #544: `NestedEvent` now wraps the machine's scope into partials passed to `HierarchicalMachine._process`. This prevents queued transitions from losing their scope. - Feature #533: `(A)Graph.draw` function (object returned by `GraphMachine.get_graph()`) can be passed a file/stream object as first parameter or `None`. The later will result in `draw` returning a binary string. (thanks @Blindfreddy). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/PKG-INFO new/transitions-0.8.10/PKG-INFO --- old/transitions-0.8.9/PKG-INFO 2021-09-02 12:42:49.032455000 +0200 +++ new/transitions-0.8.10/PKG-INFO 2021-10-04 17:28:24.455153700 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: transitions -Version: 0.8.9 +Version: 0.8.10 Summary: A lightweight, object-oriented Python state machine implementation with many extensions. Home-page: http://github.com/pytransitions/transitions Author: Tal Yarkoni @@ -8,7 +8,7 @@ Maintainer: Alexander Neumann Maintainer-email: alen...@gmail.com License: MIT -Download-URL: https://github.com/pytransitions/transitions/archive/0.8.9.tar.gz +Download-URL: https://github.com/pytransitions/transitions/archive/0.8.10.tar.gz Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 @@ -959,7 +959,7 @@ Here you get to consolidate all state machine functionality into your existing model, which often feels more natural way than sticking all of the functionality we want in a separate standalone `Machine` instance. A machine can handle multiple models which can be passed as a list like `Machine(model=[model1, model2, ...])`. -In cases where you want to add models *as well as* the machine instance itself, you can pass the string placeholder `'self'` during initialization like `Machine(model=['self', model1, ...])`. +In cases where you want to add models *as well as* the machine instance itself, you can pass the class variable placeholder (string) `Machine.self_literal` during initialization like `Machine(model=[Machine.self_literal, model1, ...])`. You can also create a standalone machine, and register models dynamically via `machine.add_model` by passing `model=None` to the constructor. Furthermore, you can use `machine.dispatch` to trigger events on all currently added models. Remember to call `machine.remove_model` if machine is long-lasting and your models are temporary and should be garbage collected: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/README.md new/transitions-0.8.10/README.md --- old/transitions-0.8.9/README.md 2021-09-02 12:29:10.000000000 +0200 +++ new/transitions-0.8.10/README.md 2021-09-10 14:13:06.000000000 +0200 @@ -1,9 +1,9 @@ # <a name="transitions-module"></a> transitions -[](https://github.com/pytransitions/transitions) +[](https://github.com/pytransitions/transitions) [](https://github.com/pytransitions/transitions/actions?query=workflow%3Apytest) [](https://coveralls.io/github/pytransitions/transitions?branch=master) [](https://pypi.org/project/transitions) -[](https://github.com/pytransitions/transitions/compare/0.8.8...master) +[](https://github.com/pytransitions/transitions/compare/0.8.9...master) [](LICENSE) [](https://mybinder.org/v2/gh/pytransitions/transitions/master?filepath=examples%2FPlayground.ipynb) <!-- [](https://github.com/pytransitions/transitions) --> @@ -987,7 +987,7 @@ Here you get to consolidate all state machine functionality into your existing model, which often feels more natural way than sticking all of the functionality we want in a separate standalone `Machine` instance. A machine can handle multiple models which can be passed as a list like `Machine(model=[model1, model2, ...])`. -In cases where you want to add models *as well as* the machine instance itself, you can pass the string placeholder `'self'` during initialization like `Machine(model=['self', model1, ...])`. +In cases where you want to add models *as well as* the machine instance itself, you can pass the class variable placeholder (string) `Machine.self_literal` during initialization like `Machine(model=[Machine.self_literal, model1, ...])`. You can also create a standalone machine, and register models dynamically via `machine.add_model` by passing `model=None` to the constructor. Furthermore, you can use `machine.dispatch` to trigger events on all currently added models. Remember to call `machine.remove_model` if machine is long-lasting and your models are temporary and should be garbage collected: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' "old/transitions-0.8.9/examples/Frequently asked questions.ipynb" "new/transitions-0.8.10/examples/Frequently asked questions.ipynb" --- "old/transitions-0.8.9/examples/Frequently asked questions.ipynb" 2021-06-28 17:11:49.000000000 +0200 +++ "new/transitions-0.8.10/examples/Frequently asked questions.ipynb" 2021-09-10 14:13:06.000000000 +0200 @@ -546,7 +546,7 @@ " # override Machine.add_model to assign 'can_trigger' to the model\n", " def add_model(self, model, initial=None):\n", " for mod in listify(model):\n", - " mod = self if mod == 'self' else mod\n", + " mod = self if mod is self.self_literal else mod\n", " if mod not in self.models:\n", " setattr(mod, 'can_trigger', partial(self._can_trigger, mod))\n", " super(PeekMachine, self).add_model(mod, initial)\n", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/tests/test_core.py new/transitions-0.8.10/tests/test_core.py --- old/transitions-0.8.9/tests/test_core.py 2021-06-28 16:44:15.000000000 +0200 +++ new/transitions-0.8.10/tests/test_core.py 2021-09-10 14:13:06.000000000 +0200 @@ -348,7 +348,7 @@ def test_auto_transitions(self): states = ['A', {'name': 'B'}, State(name='C')] - m = Machine('self', states, initial='A', auto_transitions=True) + m = Machine(states=states, initial='A', auto_transitions=True) m.to_B() self.assertEqual(m.state, 'B') m.to_C() @@ -356,13 +356,13 @@ m.to_A() self.assertEqual(m.state, 'A') # Should fail if auto transitions is off... - m = Machine('self', states, initial='A', auto_transitions=False) + m = Machine(states=states, initial='A', auto_transitions=False) with self.assertRaises(AttributeError): m.to_C() def test_ordered_transitions(self): states = ['beginning', 'middle', 'end'] - m = Machine('self', states) + m = Machine(states=states) m.add_ordered_transitions() self.assertEqual(m.state, 'initial') m.next_state() @@ -374,21 +374,21 @@ self.assertEqual(m.state, 'initial') # Include initial state in loop - m = Machine('self', states) + m = Machine(states=states) m.add_ordered_transitions(loop_includes_initial=False) m.to_end() m.next_state() self.assertEqual(m.state, 'beginning') # Do not loop transitions - m = Machine('self', states) + m = Machine(states=states) m.add_ordered_transitions(loop=False) m.to_end() with self.assertRaises(MachineError): m.next_state() # Test user-determined sequence and trigger name - m = Machine('self', states, initial='beginning') + m = Machine(states=states, initial='beginning') m.add_ordered_transitions(['end', 'beginning'], trigger='advance') m.advance() self.assertEqual(m.state, 'end') @@ -396,19 +396,19 @@ self.assertEqual(m.state, 'beginning') # Via init argument - m = Machine('self', states, initial='beginning', ordered_transitions=True) + m = Machine(states=states, initial='beginning', ordered_transitions=True) m.next_state() self.assertEqual(m.state, 'middle') # Alter initial state - m = Machine('self', states, initial='middle', ordered_transitions=True) + m = Machine(states=states, initial='middle', ordered_transitions=True) m.next_state() self.assertEqual(m.state, 'end') m.next_state() self.assertEqual(m.state, 'beginning') # Partial state machine without the initial state - m = Machine('self', states, initial='beginning') + m = Machine(states=states, initial='beginning') m.add_ordered_transitions(['middle', 'end']) self.assertEqual(m.state, 'beginning') with self.assertRaises(MachineError): @@ -433,17 +433,17 @@ transitions = [['a_to_b', 'A', 'B']] # Exception is triggered by default b_state = State('B') - m1 = Machine('self', states=[a_state, b_state], transitions=transitions, + m1 = Machine(states=[a_state, b_state], transitions=transitions, initial='B') with self.assertRaises(MachineError): m1.a_to_b() # Set default value on machine level - m2 = Machine('self', states=[a_state, b_state], transitions=transitions, + m2 = Machine(states=[a_state, b_state], transitions=transitions, initial='B', ignore_invalid_triggers=True) m2.a_to_b() # Exception is suppressed, so this passes b_state = State('B', ignore_invalid_triggers=True) - m3 = Machine('self', states=[a_state, b_state], transitions=transitions, + m3 = Machine(states=[a_state, b_state], transitions=transitions, initial='B') m3.a_to_b() # Set for some states but not others @@ -455,7 +455,7 @@ with self.assertRaises(MachineError): m1.a_to_b() # State value overrides machine behaviour - m3 = Machine('self', states=[a_state, b_state], transitions=transitions, + m3 = Machine(states=[a_state, b_state], transitions=transitions, initial='B', ignore_invalid_triggers=False) m3.a_to_b() @@ -483,7 +483,7 @@ before_state_change = MagicMock() after_state_change = MagicMock() - m = Machine('self', states=['A', 'B'], + m = Machine(states=['A', 'B'], before_state_change=before_state_change, after_state_change=after_state_change, send_event=True, initial='A', auto_transitions=True) @@ -1063,7 +1063,7 @@ def test_get_transitions(self): states = ['A', 'B', 'C', 'D'] - m = self.machine_cls('self', states, initial='A', auto_transitions=False) + m = self.machine_cls(states=states, initial='A', auto_transitions=False) m.add_transition('go', ['A', 'B', 'C'], 'D') m.add_transition('run', 'A', 'D') self.assertEqual( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/tests/test_nesting.py new/transitions-0.8.10/tests/test_nesting.py --- old/transitions-0.8.9/tests/test_nesting.py 2021-09-02 11:17:59.000000000 +0200 +++ new/transitions-0.8.10/tests/test_nesting.py 2021-09-22 15:11:26.000000000 +0200 @@ -271,14 +271,14 @@ self.assertEqual('ninth', m.state) # Include initial state in loop - m = self.stuff.machine_cls('self', states) + m = self.stuff.machine_cls(states=states) m.add_ordered_transitions(loop_includes_initial=False) m.to_ninth() m.next_state() self.assertEqual(m.state, 'first') # Test user-determined sequence and trigger name - m = self.stuff.machine_cls('self', states, initial='first') + m = self.stuff.machine_cls(states=states, initial='first') m.add_ordered_transitions(['first', 'ninth'], trigger='advance') m.advance() self.assertEqual(m.state, 'ninth') @@ -286,7 +286,7 @@ self.assertEqual(m.state, 'first') # Via init argument - m = self.stuff.machine_cls('self', states, initial='first', ordered_transitions=True) + m = self.stuff.machine_cls(states=states, initial='first', ordered_transitions=True) m.next_state() self.assertEqual(m.state, 'first{0}second'.format(State.separator)) @@ -596,6 +596,37 @@ machine.to_A() self.assertEqual("A{0}2".format(self.state_cls.separator), machine.state) + def test_nested_transitions(self): + states = [{ + 'name': 'A', + 'states': [ + {'name': 'B', + 'states': [ + {'name': 'C', + 'states': ['1', '2'], + 'initial': '1'}], + 'transitions': [['go', 'C_1', 'C_2']], + 'initial': 'C', + }], + 'initial': 'B' + }] + machine = self.machine_cls(states=states, initial='A') + machine.go() + + def test_auto_transitions_from_nested_callback(self): + + def fail(): + self.fail("C should not be exited!") + + states = [ + {'name': 'b', 'children': [ + {'name': 'c', 'on_exit': fail, 'on_enter': 'to_b_c_ca', 'children': ['ca', 'cb']}, + 'd' + ]}, + ] + machine = self.machine_cls(states=states, queued=True, initial='b') + machine.to_b_c() + class TestSeparatorsBase(TestCase): @@ -723,7 +754,7 @@ states = ['A', 'B', {'name': 'C', 'children': ['1', '2', {'name': '3', 'children': ['a', 'b', 'c']}]}, 'D', 'E', 'F'] - machine = CustomHierarchicalGraphMachine('self', states, initial='A', auto_transitions=False, + machine = CustomHierarchicalGraphMachine(states=states, initial='A', auto_transitions=False, ignore_invalid_triggers=True, use_pygraphviz=False) machine.add_ordered_transitions(trigger='next_state') machine.next_state() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/tests/test_nesting_legacy.py new/transitions-0.8.10/tests/test_nesting_legacy.py --- old/transitions-0.8.9/tests/test_nesting_legacy.py 2021-09-02 12:00:32.000000000 +0200 +++ new/transitions-0.8.10/tests/test_nesting_legacy.py 2021-09-22 15:04:35.000000000 +0200 @@ -106,6 +106,9 @@ def test_queued_callbacks(self): pass # not supported by legacy machine + def test_nested_transitions(self): + pass # not supported by legacy machine + class TestReuseLegacySeparatorDefault(TestReuseSeparatorBase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/tests/test_reuse.py new/transitions-0.8.10/tests/test_reuse.py --- old/transitions-0.8.9/tests/test_reuse.py 2021-01-18 15:27:04.000000000 +0100 +++ new/transitions-0.8.10/tests/test_reuse.py 2021-09-22 15:04:35.000000000 +0200 @@ -1,327 +1,327 @@ -try: - from builtins import object -except ImportError: - pass - -from transitions import MachineError -from transitions.extensions import MachineFactory -from transitions.extensions.nesting import NestedState - -from .utils import Stuff - -from unittest import TestCase - -try: - from unittest.mock import MagicMock -except ImportError: - from mock import MagicMock - - -test_states = ['A', 'B', {'name': 'C', 'children': ['1', '2', {'name': '3', 'children': ['a', 'b', 'c']}]}, - 'D', 'E', 'F'] - - -class TestReuseSeparatorBase(TestCase): - separator = '_' - - def setUp(self): - - class CustomState(NestedState): - separator = self.separator - - class CustomMachine(MachineFactory.get_predefined(nested=True)): - state_cls = CustomState - - self.states = test_states - self.machine_cls = CustomMachine - self.state_cls = self.machine_cls.state_cls - self.stuff = Stuff(self.states, self.machine_cls) - - def test_wrong_nesting(self): - - correct = ['A', {'name': 'B', 'children': self.stuff.machine}] - wrong_type = ['A', {'name': 'B', 'children': self.stuff}] - siblings = ['A', {'name': 'B', 'children': ['1', self.stuff.machine]}] - collision = ['A', {'name': 'B', 'children': ['A', self.stuff.machine]}] - - m = self.machine_cls(states=correct) - if m.state_cls.separator != '_': - m.to_B.C.s3.a() - else: - m.to_B_C_3_a() - - with self.assertRaises(ValueError): - m = self.machine_cls(states=wrong_type) - - with self.assertRaises(ValueError): - m = self.machine_cls(states=collision) - - m = self.machine_cls(states=siblings) - if m.state_cls.separator != '_': - m.to_B.s1() - m.to_B.A() - else: - m.to_B_1() - m.to_B_A() - - -class TestReuseSeparatorDot(TestReuseSeparatorBase): - separator = '.' - - -class TestReuse(TestCase): - - def setUp(self): - self.states = test_states - self.machine_cls = MachineFactory.get_predefined(nested=True) - self.state_cls = self.machine_cls.state_cls - self.stuff = Stuff(self.states, self.machine_cls) - - def test_blueprint_reuse(self): - State = self.state_cls - states = ['1', '2', '3'] - transitions = [ - {'trigger': 'increase', 'source': '1', 'dest': '2'}, - {'trigger': 'increase', 'source': '2', 'dest': '3'}, - {'trigger': 'decrease', 'source': '3', 'dest': '2'}, - {'trigger': 'decrease', 'source': '1', 'dest': '1'}, - {'trigger': 'reset', 'source': '*', 'dest': '1'}, - ] - - counter = self.machine_cls(states=states, transitions=transitions, before_state_change='check', - after_state_change='clear', initial='1') - - new_states = ['A', 'B', {'name': 'C', 'children': counter}] - new_transitions = [ - {'trigger': 'forward', 'source': 'A', 'dest': 'B'}, - {'trigger': 'forward', 'source': 'B', 'dest': 'C%s1' % State.separator}, - {'trigger': 'backward', 'source': 'C', 'dest': 'B'}, - {'trigger': 'backward', 'source': 'B', 'dest': 'A'}, - {'trigger': 'calc', 'source': '*', 'dest': 'C'}, - ] - - walker = self.machine_cls(states=new_states, transitions=new_transitions, before_state_change='watch', - after_state_change='look_back', initial='A') - - walker.watch = lambda: 'walk' - walker.look_back = lambda: 'look_back' - walker.check = lambda: 'check' - walker.clear = lambda: 'clear' - - with self.assertRaises(MachineError): - walker.increase() - self.assertEqual(walker.state, 'A') - walker.forward() - walker.forward() - self.assertEqual(walker.state, 'C%s1' % State.separator) - walker.increase() - self.assertEqual(walker.state, 'C%s2' % State.separator) - walker.reset() - self.assertEqual(walker.state, 'C%s1' % State.separator) - walker.to_A() - self.assertEqual(walker.state, 'A') - walker.calc() - self.assertEqual(walker.state, 'C{0}1'.format(State.separator)) - - def test_blueprint_initial_false(self): - child = self.machine_cls(states=['A', 'B'], initial='A') - parent = self.machine_cls(states=['a', 'b', {'name': 'c', 'children': child, 'initial': False}]) - parent.to_c() - self.assertEqual(parent.state, 'c') - - def test_blueprint_remap(self): - State = self.state_cls - states = ['1', '2', '3', 'finished'] - transitions = [ - {'trigger': 'increase', 'source': '1', 'dest': '2'}, - {'trigger': 'increase', 'source': '2', 'dest': '3'}, - {'trigger': 'decrease', 'source': '3', 'dest': '2'}, - {'trigger': 'decrease', 'source': '1', 'dest': '1'}, - {'trigger': 'reset', 'source': '*', 'dest': '1'}, - {'trigger': 'done', 'source': '3', 'dest': 'finished'} - ] - - counter = self.machine_cls(states=states, transitions=transitions, initial='1') - - new_states = ['A', 'B', {'name': 'C', 'children': - [counter, {'name': 'X', 'children': ['will', 'be', 'filtered', 'out']}], - 'remap': {'finished': 'A', 'X': 'A'}}] - new_transitions = [ - {'trigger': 'forward', 'source': 'A', 'dest': 'B'}, - {'trigger': 'forward', 'source': 'B', 'dest': 'C%s1' % State.separator}, - {'trigger': 'backward', 'source': 'C', 'dest': 'B'}, - {'trigger': 'backward', 'source': 'B', 'dest': 'A'}, - {'trigger': 'calc', 'source': '*', 'dest': 'C%s1' % State.separator}, - ] - - walker = self.machine_cls(states=new_states, transitions=new_transitions, before_state_change='watch', - after_state_change='look_back', initial='A') - - walker.watch = lambda: 'walk' - walker.look_back = lambda: 'look_back' - - counter.increase() - counter.increase() - counter.done() - self.assertEqual(counter.state, 'finished') - - with self.assertRaises(MachineError): - walker.increase() - self.assertEqual(walker.state, 'A') - walker.forward() - walker.forward() - self.assertEqual(walker.state, 'C%s1' % State.separator) - walker.increase() - self.assertEqual(walker.state, 'C%s2' % State.separator) - walker.reset() - self.assertEqual(walker.state, 'C%s1' % State.separator) - walker.to_A() - self.assertEqual(walker.state, 'A') - walker.calc() - self.assertEqual(walker.state, 'C%s1' % State.separator) - walker.increase() - walker.increase() - walker.done() - self.assertEqual(walker.state, 'A') - self.assertFalse('C.finished' in walker.states) - - def test_example_reuse(self): - State = self.state_cls - count_states = ['1', '2', '3', 'done'] - count_trans = [ - ['increase', '1', '2'], - ['increase', '2', '3'], - ['decrease', '3', '2'], - ['decrease', '2', '1'], - {'trigger': 'done', 'source': '3', 'dest': 'done', 'conditions': 'this_passes'}, - ] - - counter = self.machine_cls(states=count_states, transitions=count_trans, initial='1') - counter.increase() # love my counter - states = ['waiting', 'collecting', {'name': 'counting', 'children': counter}] - states_remap = ['waiting', 'collecting', {'name': 'counting', 'children': counter, 'remap': {'done': 'waiting'}}] - - transitions = [ - ['collect', '*', 'collecting'], - ['wait', '*', 'waiting'], - ['count', '*', 'counting%s1' % State.separator] - ] - - collector = self.stuff.machine_cls(states=states, transitions=transitions, initial='waiting') - collector.this_passes = self.stuff.this_passes - collector.collect() # collecting - collector.count() # let's see what we got - collector.increase() # counting_2 - collector.increase() # counting_3 - collector.done() # counting_done - self.assertEqual(collector.state, 'counting{0}done'.format(State.separator)) - collector.wait() # go back to waiting - self.assertEqual(collector.state, 'waiting') - - # reuse counter instance with remap - collector = self.machine_cls(states=states_remap, transitions=transitions, initial='waiting') - collector.this_passes = self.stuff.this_passes - collector.collect() # collecting - collector.count() # let's see what we got - collector.increase() # counting_2 - collector.increase() # counting_3 - collector.done() # counting_done - self.assertEqual(collector.state, 'waiting') - - # # same as above but with states and therefore stateless embedding - states_remap[2]['children'] = count_states - transitions.append(['increase', 'counting%s1' % State.separator, 'counting%s2' % State.separator]) - transitions.append(['increase', 'counting%s2' % State.separator, 'counting%s3' % State.separator]) - transitions.append(['done', 'counting%s3' % State.separator, 'waiting']) - - collector = self.machine_cls(states=states_remap, transitions=transitions, initial='waiting') - collector.collect() # collecting - collector.count() # let's see what we got - collector.increase() # counting_2 - collector.increase() # counting_3 - collector.done() # counting_done - self.assertEqual(collector.state, 'waiting') - - # check if counting_done was correctly omitted - collector.add_transition('fail', '*', 'counting%sdone' % State.separator) - with self.assertRaises(ValueError): - collector.fail() - - def test_reuse_prepare(self): - class Model: - def __init__(self): - self.prepared = False - - def preparation(self): - self.prepared = True - - ms_model = Model() - ms = self.machine_cls(ms_model, states=["C", "D"], - transitions={"trigger": "go", "source": "*", "dest": "D", - "prepare": "preparation"}, initial="C") - ms_model.go() - self.assertTrue(ms_model.prepared) - - m_model = Model() - m = self.machine_cls(m_model, states=["A", "B", {"name": "NEST", "children": ms}]) - m_model.to('NEST%sC' % self.state_cls.separator) - m_model.go() - self.assertTrue(m_model.prepared) - - def test_reuse_self_reference(self): - separator = self.state_cls.separator - - class Nested(self.machine_cls): - - def __init__(self, parent): - self.parent = parent - self.mock = MagicMock() - states = ['1', '2'] - transitions = [{'trigger': 'finish', 'source': '*', 'dest': '2', 'after': self.print_msg}] - super(Nested, self).__init__(states=states, transitions=transitions, initial='1') - - def print_msg(self): - self.mock() - self.parent.print_top() - - class Top(self.machine_cls): - - def print_msg(self): - self.mock() - - def __init__(self): - self.nested = Nested(self) - self.mock = MagicMock() - - states = ['A', {'name': 'B', 'children': self.nested}] - transitions = [dict(trigger='print_top', source='*', dest='=', after=self.print_msg), - dict(trigger='to_nested', source='*', dest='B{0}1'.format(separator))] - - super(Top, self).__init__(states=states, transitions=transitions, initial='A') - - top_machine = Top() - self.assertEqual(top_machine, top_machine.nested.parent) - - top_machine.to_nested() - top_machine.finish() - self.assertTrue(top_machine.mock.called) - self.assertTrue(top_machine.nested.mock.called) - self.assertIs(top_machine.nested.get_state('2').on_enter, - top_machine.get_state('B{0}2'.format(separator)).on_enter) - - def test_reuse_machine_config(self): - simple_config = { - "name": "Child", - "states": ["1", "2"], - "transitions": [['go', '1', '2']], - "initial": "1" - } - simple_cls = MachineFactory.get_predefined() - simple = simple_cls(**simple_config) - self.assertTrue(simple.is_1()) - self.assertTrue(simple.go()) - self.assertTrue(simple.is_2()) - machine = self.machine_cls(states=['A', simple_config], initial='A') - machine.to_Child() - machine.go() - self.assertTrue(machine.is_Child_2()) +try: + from builtins import object +except ImportError: + pass + +from transitions import MachineError +from transitions.extensions import MachineFactory +from transitions.extensions.nesting import NestedState + +from .utils import Stuff + +from unittest import TestCase + +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock + + +test_states = ['A', 'B', {'name': 'C', 'children': ['1', '2', {'name': '3', 'children': ['a', 'b', 'c']}]}, + 'D', 'E', 'F'] + + +class TestReuseSeparatorBase(TestCase): + separator = '_' + + def setUp(self): + + class CustomState(NestedState): + separator = self.separator + + class CustomMachine(MachineFactory.get_predefined(nested=True)): + state_cls = CustomState + + self.states = test_states + self.machine_cls = CustomMachine + self.state_cls = self.machine_cls.state_cls + self.stuff = Stuff(self.states, self.machine_cls) + + def test_wrong_nesting(self): + + correct = ['A', {'name': 'B', 'children': self.stuff.machine}] + wrong_type = ['A', {'name': 'B', 'children': self.stuff}] + siblings = ['A', {'name': 'B', 'children': ['1', self.stuff.machine]}] + collision = ['A', {'name': 'B', 'children': ['A', self.stuff.machine]}] + + m = self.machine_cls(states=correct) + if m.state_cls.separator != '_': + m.to_B.C.s3.a() + else: + m.to_B_C_3_a() + + with self.assertRaises(ValueError): + m = self.machine_cls(states=wrong_type) + + with self.assertRaises(ValueError): + m = self.machine_cls(states=collision) + + m = self.machine_cls(states=siblings) + if m.state_cls.separator != '_': + m.to_B.s1() + m.to_B.A() + else: + m.to_B_1() + m.to_B_A() + + +class TestReuseSeparatorDot(TestReuseSeparatorBase): + separator = '.' + + +class TestReuse(TestCase): + + def setUp(self): + self.states = test_states + self.machine_cls = MachineFactory.get_predefined(nested=True) + self.state_cls = self.machine_cls.state_cls + self.stuff = Stuff(self.states, self.machine_cls) + + def test_blueprint_reuse(self): + State = self.state_cls + states = ['1', '2', '3'] + transitions = [ + {'trigger': 'increase', 'source': '1', 'dest': '2'}, + {'trigger': 'increase', 'source': '2', 'dest': '3'}, + {'trigger': 'decrease', 'source': '3', 'dest': '2'}, + {'trigger': 'decrease', 'source': '1', 'dest': '1'}, + {'trigger': 'reset', 'source': '*', 'dest': '1'}, + ] + + counter = self.machine_cls(states=states, transitions=transitions, before_state_change='check', + after_state_change='clear', initial='1') + + new_states = ['A', 'B', {'name': 'C', 'children': counter}] + new_transitions = [ + {'trigger': 'forward', 'source': 'A', 'dest': 'B'}, + {'trigger': 'forward', 'source': 'B', 'dest': 'C%s1' % State.separator}, + {'trigger': 'backward', 'source': 'C', 'dest': 'B'}, + {'trigger': 'backward', 'source': 'B', 'dest': 'A'}, + {'trigger': 'calc', 'source': '*', 'dest': 'C'}, + ] + + walker = self.machine_cls(states=new_states, transitions=new_transitions, before_state_change='watch', + after_state_change='look_back', initial='A') + + walker.watch = lambda: 'walk' + walker.look_back = lambda: 'look_back' + walker.check = lambda: 'check' + walker.clear = lambda: 'clear' + + with self.assertRaises(MachineError): + walker.increase() + self.assertEqual(walker.state, 'A') + walker.forward() + walker.forward() + self.assertEqual(walker.state, 'C%s1' % State.separator) + walker.increase() + self.assertEqual(walker.state, 'C%s2' % State.separator) + walker.reset() + self.assertEqual(walker.state, 'C%s1' % State.separator) + walker.to_A() + self.assertEqual(walker.state, 'A') + walker.calc() + self.assertEqual(walker.state, 'C{0}1'.format(State.separator)) + + def test_blueprint_initial_false(self): + child = self.machine_cls(states=['A', 'B'], initial='A') + parent = self.machine_cls(states=['a', 'b', {'name': 'c', 'children': child, 'initial': False}]) + parent.to_c() + self.assertEqual(parent.state, 'c') + + def test_blueprint_remap(self): + State = self.state_cls + states = ['1', '2', '3', 'finished'] + transitions = [ + {'trigger': 'increase', 'source': '1', 'dest': '2'}, + {'trigger': 'increase', 'source': '2', 'dest': '3'}, + {'trigger': 'decrease', 'source': '3', 'dest': '2'}, + {'trigger': 'decrease', 'source': '1', 'dest': '1'}, + {'trigger': 'reset', 'source': '*', 'dest': '1'}, + {'trigger': 'done', 'source': '3', 'dest': 'finished'} + ] + + counter = self.machine_cls(states=states, transitions=transitions, initial='1') + + new_states = ['A', 'B', {'name': 'C', 'children': + [counter, {'name': 'X', 'children': ['will', 'be', 'filtered', 'out']}], + 'remap': {'finished': 'A', 'X': 'A'}}] + new_transitions = [ + {'trigger': 'forward', 'source': 'A', 'dest': 'B'}, + {'trigger': 'forward', 'source': 'B', 'dest': 'C%s1' % State.separator}, + {'trigger': 'backward', 'source': 'C', 'dest': 'B'}, + {'trigger': 'backward', 'source': 'B', 'dest': 'A'}, + {'trigger': 'calc', 'source': '*', 'dest': 'C%s1' % State.separator}, + ] + + walker = self.machine_cls(states=new_states, transitions=new_transitions, before_state_change='watch', + after_state_change='look_back', initial='A') + + walker.watch = lambda: 'walk' + walker.look_back = lambda: 'look_back' + + counter.increase() + counter.increase() + counter.done() + self.assertEqual(counter.state, 'finished') + + with self.assertRaises(MachineError): + walker.increase() + self.assertEqual(walker.state, 'A') + walker.forward() + walker.forward() + self.assertEqual(walker.state, 'C%s1' % State.separator) + walker.increase() + self.assertEqual(walker.state, 'C%s2' % State.separator) + walker.reset() + self.assertEqual(walker.state, 'C%s1' % State.separator) + walker.to_A() + self.assertEqual(walker.state, 'A') + walker.calc() + self.assertEqual(walker.state, 'C%s1' % State.separator) + walker.increase() + walker.increase() + walker.done() + self.assertEqual(walker.state, 'A') + self.assertFalse('C.finished' in walker.states) + + def test_example_reuse(self): + State = self.state_cls + count_states = ['1', '2', '3', 'done'] + count_trans = [ + ['increase', '1', '2'], + ['increase', '2', '3'], + ['decrease', '3', '2'], + ['decrease', '2', '1'], + {'trigger': 'done', 'source': '3', 'dest': 'done', 'conditions': 'this_passes'}, + ] + + counter = self.machine_cls(states=count_states, transitions=count_trans, initial='1') + counter.increase() # love my counter + states = ['waiting', 'collecting', {'name': 'counting', 'children': counter}] + states_remap = ['waiting', 'collecting', {'name': 'counting', 'children': counter, 'remap': {'done': 'waiting'}}] + + transitions = [ + ['collect', '*', 'collecting'], + ['wait', '*', 'waiting'], + ['count', '*', 'counting%s1' % State.separator] + ] + + collector = self.stuff.machine_cls(states=states, transitions=transitions, initial='waiting') + collector.this_passes = self.stuff.this_passes + collector.collect() # collecting + collector.count() # let's see what we got + collector.increase() # counting_2 + collector.increase() # counting_3 + collector.done() # counting_done + self.assertEqual(collector.state, 'counting{0}done'.format(State.separator)) + collector.wait() # go back to waiting + self.assertEqual(collector.state, 'waiting') + + # reuse counter instance with remap + collector = self.machine_cls(states=states_remap, transitions=transitions, initial='waiting') + collector.this_passes = self.stuff.this_passes + collector.collect() # collecting + collector.count() # let's see what we got + collector.increase() # counting_2 + collector.increase() # counting_3 + collector.done() # counting_done + self.assertEqual(collector.state, 'waiting') + + # # same as above but with states and therefore stateless embedding + states_remap[2]['children'] = count_states + transitions.append(['increase', 'counting%s1' % State.separator, 'counting%s2' % State.separator]) + transitions.append(['increase', 'counting%s2' % State.separator, 'counting%s3' % State.separator]) + transitions.append(['done', 'counting%s3' % State.separator, 'waiting']) + + collector = self.machine_cls(states=states_remap, transitions=transitions, initial='waiting') + collector.collect() # collecting + collector.count() # let's see what we got + collector.increase() # counting_2 + collector.increase() # counting_3 + collector.done() # counting_done + self.assertEqual(collector.state, 'waiting') + + # check if counting_done was correctly omitted + collector.add_transition('fail', '*', 'counting%sdone' % State.separator) + with self.assertRaises(ValueError): + collector.fail() + + def test_reuse_prepare(self): + class Model: + def __init__(self): + self.prepared = False + + def preparation(self): + self.prepared = True + + ms_model = Model() + ms = self.machine_cls(ms_model, states=["C", "D"], + transitions={"trigger": "go", "source": "*", "dest": "D", + "prepare": "preparation"}, initial="C") + ms_model.go() + self.assertTrue(ms_model.prepared) + + m_model = Model() + m = self.machine_cls(m_model, states=["A", "B", {"name": "NEST", "children": ms}]) + m_model.to('NEST%sC' % self.state_cls.separator) + m_model.go() + self.assertTrue(m_model.prepared) + + def test_reuse_self_reference(self): + separator = self.state_cls.separator + + class Nested(self.machine_cls): + + def __init__(self, parent): + self.parent = parent + self.mock = MagicMock() + states = ['1', '2'] + transitions = [{'trigger': 'finish', 'source': '*', 'dest': '2', 'after': self.print_msg}] + super(Nested, self).__init__(states=states, transitions=transitions, initial='1') + + def print_msg(self): + self.mock() + self.parent.print_top() + + class Top(self.machine_cls): + + def print_msg(self): + self.mock() + + def __init__(self): + self.nested = Nested(self) + self.mock = MagicMock() + + states = ['A', {'name': 'B', 'children': self.nested}] + transitions = [dict(trigger='print_top', source='*', dest='=', after=self.print_msg), + dict(trigger='to_nested', source='*', dest='B{0}1'.format(separator))] + + super(Top, self).__init__(states=states, transitions=transitions, initial='A') + + top_machine = Top() + self.assertEqual(top_machine, top_machine.nested.parent) + + top_machine.to_nested() + top_machine.finish() + self.assertTrue(top_machine.mock.called) + self.assertTrue(top_machine.nested.mock.called) + self.assertIs(top_machine.nested.get_state('2').on_enter, + top_machine.get_state('B{0}2'.format(separator)).on_enter) + + def test_reuse_machine_config(self): + simple_config = { + "name": "Child", + "states": ["1", "2"], + "transitions": [['go', '1', '2']], + "initial": "1" + } + simple_cls = MachineFactory.get_predefined() + simple = simple_cls(**simple_config) + self.assertTrue(simple.is_1()) + self.assertTrue(simple.go()) + self.assertTrue(simple.is_2()) + machine = self.machine_cls(states=['A', simple_config], initial='A') + machine.to_Child() + machine.go() + self.assertTrue(machine.is_Child_2()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions/core.py new/transitions-0.8.10/transitions/core.py --- old/transitions-0.8.9/transitions/core.py 2021-09-02 12:15:39.000000000 +0200 +++ new/transitions-0.8.10/transitions/core.py 2021-09-10 14:13:13.000000000 +0200 @@ -502,8 +502,9 @@ state_cls = State transition_cls = Transition event_cls = Event + self_literal = 'self' - def __init__(self, model='self', states=None, initial='initial', transitions=None, + def __init__(self, model=self_literal, states=None, initial='initial', transitions=None, send_event=False, auto_transitions=True, ordered_transitions=False, ignore_invalid_triggers=None, before_state_change=None, after_state_change=None, name=None, @@ -511,8 +512,8 @@ **kwargs): """ Args: - model (object or list): The object(s) whose states we want to manage. If 'self', - the current Machine instance will be used the model (i.e., all + model (object or list): The object(s) whose states we want to manage. If set to `Machine.self_literal` + (default value), the current Machine instance will be used as the model (i.e., all triggering events will be attached to the Machine itself). Note that an empty list is treated like no model. states (list or Enum): A list or enumeration of valid states. Each list element can be either a @@ -616,7 +617,7 @@ initial = self.initial for mod in models: - mod = self if mod == 'self' else mod + mod = self if mod is self.self_literal else mod if mod not in self.models: self._checked_assignment(mod, 'trigger', partial(self._get_trigger, mod)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions/extensions/asyncio.py new/transitions-0.8.10/transitions/extensions/asyncio.py --- old/transitions-0.8.9/transitions/extensions/asyncio.py 2021-06-28 16:35:09.000000000 +0200 +++ new/transitions-0.8.10/transitions/extensions/asyncio.py 2021-09-22 15:04:35.000000000 +0200 @@ -326,7 +326,7 @@ super().add_model(model, initial) if self.has_queue == 'model': for mod in listify(model): - self._transition_queue_dict[id(mod) if mod != 'self' else id(self)] = deque() + self._transition_queue_dict[id(self) if mod is self.self_literal else id(mod)] = deque() async def dispatch(self, trigger, *args, **kwargs): # ToDo: not tested """ Trigger an event on all models assigned to the machine. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions/extensions/diagrams.py new/transitions-0.8.10/transitions/extensions/diagrams.py --- old/transitions-0.8.9/transitions/extensions/diagrams.py 2021-06-28 16:35:09.000000000 +0200 +++ new/transitions-0.8.10/transitions/extensions/diagrams.py 2021-09-22 15:04:35.000000000 +0200 @@ -224,7 +224,7 @@ models = listify(model) super(GraphMachine, self).add_model(models, initial) for mod in models: - mod = self if mod == 'self' else mod + mod = self if mod is self.self_literal else mod if hasattr(mod, 'get_graph'): raise AttributeError('Model already has a get_graph attribute. Graph retrieval cannot be bound.') setattr(mod, 'get_graph', partial(self._get_graph, mod)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions/extensions/locking.py new/transitions-0.8.10/transitions/extensions/locking.py --- old/transitions-0.8.9/transitions/extensions/locking.py 2021-06-28 16:35:09.000000000 +0200 +++ new/transitions-0.8.10/transitions/extensions/locking.py 2021-09-10 14:13:13.000000000 +0200 @@ -145,7 +145,7 @@ output = _super(LockedMachine, self).add_model(models, initial) for mod in models: - mod = self if mod == 'self' else mod + mod = self if mod is self.self_literal else mod self.model_context_map[id(mod)].extend(self.machine_context) self.model_context_map[id(mod)].extend(model_context) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions/extensions/nesting.py new/transitions-0.8.10/transitions/extensions/nesting.py --- old/transitions-0.8.9/transitions/extensions/nesting.py 2021-09-02 12:21:36.000000000 +0200 +++ new/transitions-0.8.10/transitions/extensions/nesting.py 2021-09-22 15:11:46.000000000 +0200 @@ -109,7 +109,8 @@ """ # Save the current scope (_machine.scoped, _machine.states, _machine.events) in partial # since queued transitions could otherwise loose their scope. - func = partial(self._trigger, _model, _machine, (_machine.scoped, _machine.states, _machine.events), + func = partial(self._trigger, _model, _machine, + (_machine.scoped, _machine.states, _machine.events, _machine.prefix_path), *args, **kwargs) # pylint: disable=protected-access # noinspection PyProtectedMember @@ -272,7 +273,7 @@ def _resolve_transition(self, event_data): machine = event_data.machine - dst_name_path = machine.get_local_name(self.dest, join=False) + dst_name_path = self.dest.split(event_data.machine.state_cls.separator) _ = machine.get_state(dst_name_path) model_states = listify(getattr(event_data.model, machine.model_attribute)) state_tree = machine._build_state_tree(model_states, machine.state_cls.separator) @@ -283,10 +284,11 @@ root = src_name_path[:-1] # exit and enter the same state dst_name_path = dst_name_path[-1:] else: + tmp_tree = state_tree.get(dst_name_path[0], None) root = [] - while dst_name_path and src_name_path and src_name_path[0] == dst_name_path[0]: - root.append(src_name_path.pop(0)) - dst_name_path.pop(0) + while tmp_tree is not None: + root.append(dst_name_path.pop(0)) + tmp_tree = tmp_tree.get(dst_name_path[0], None) if len(dst_name_path) > 0 else None scoped_tree = reduce(dict.get, scope + root, state_tree) exit_partials = [partial(machine.get_state(root + state_name).scoped_exit, @@ -386,6 +388,7 @@ assert issubclass(self.event_cls, NestedEvent) assert issubclass(self.transition_cls, NestedTransition) self._stack = [] + self.prefix_path = [] self.scoped = self _super(HierarchicalMachine, self).__init__(*args, **kwargs) @@ -393,32 +396,32 @@ if isinstance(to_scope, string_types): state_name = to_scope.split(self.state_cls.separator)[0] state = self.states[state_name] - to_scope = (state, state.states, state.events) + to_scope = (state, state.states, state.events, self.prefix_path + [state_name]) elif isinstance(to_scope, Enum): state = self.states[to_scope.name] - to_scope = (state, state.states, state.events) + to_scope = (state, state.states, state.events, self.prefix_path + [to_scope.name]) elif to_scope is None: if self._stack: to_scope = self._stack[0] else: - to_scope = (self, self.states, self.events) + to_scope = (self, self.states, self.events, []) self._next_scope = to_scope return self def __enter__(self): - self._stack.append((self.scoped, self.states, self.events)) - self.scoped, self.states, self.events = self._next_scope + self._stack.append((self.scoped, self.states, self.events, self.prefix_path)) + self.scoped, self.states, self.events, self.prefix_path = self._next_scope self._next_scope = None def __exit__(self, exc_type, exc_val, exc_tb): - self.scoped, self.states, self.events = self._stack.pop() + self.scoped, self.states, self.events, self.prefix_path = self._stack.pop() def add_model(self, model, initial=None): """ Extends transitions.core.Machine.add_model by applying a custom 'to' function to the added model. """ - models = [mod if mod != 'self' else self for mod in listify(model)] + models = [self if mod is self.self_literal else mod for mod in listify(model)] _super(HierarchicalMachine, self).add_model(models, initial=initial) initial_name = getattr(models[0], self.model_attribute) if hasattr(initial_name, 'name'): @@ -612,11 +615,8 @@ _super(HierarchicalMachine, self).add_transition(trigger, source, dest, conditions, unless, before, after, prepare, **kwargs) - def get_global_name(self, state=None, join=True, scope=None): - scope = scope or self - local_stack = [s[0] for s in self._stack] + [self.scoped] - local_stack_start = len(local_stack) - local_stack[::-1].index(self) - domains = [s.name for s in local_stack[local_stack_start:]] + def get_global_name(self, state=None, join=True): + domains = copy.copy(self.prefix_path) if state: state_name = state.name if hasattr(state, 'name') else state if state_name in self.states: @@ -625,15 +625,6 @@ raise ValueError("State '{0}' not found in local states.".format(state)) return self.state_cls.separator.join(domains) if join else domains - def get_local_name(self, state_name, join=True): - state_name = state_name.split(self.state_cls.separator) - local_stack = [s[0] for s in self._stack] + [self.scoped] - local_stack_start = len(local_stack) - local_stack[::-1].index(self) - domains = [s.name for s in local_stack[local_stack_start:]] - if domains and state_name and state_name[0] != domains[0]: - return self.state_cls.separator.join(state_name) if join else state_name - return self.state_cls.separator.join(state_name) if join else state_name - def get_nested_state_names(self): ordered_states = [] for state in self.states.values(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions/extensions/nesting_legacy.py new/transitions-0.8.10/transitions/extensions/nesting_legacy.py --- old/transitions-0.8.9/transitions/extensions/nesting_legacy.py 2021-02-13 12:34:25.000000000 +0100 +++ new/transitions-0.8.10/transitions/extensions/nesting_legacy.py 2021-09-10 14:13:13.000000000 +0200 @@ -259,7 +259,7 @@ _super(HierarchicalMachine, self).add_model(model, initial=initial) models = listify(model) for mod in models: - mod = self if mod == 'self' else mod + mod = self if mod is self.self_literal else mod # TODO: Remove 'mod != self' in 0.7.0 if hasattr(mod, 'to') and mod != self: _LOGGER.warning("%sModel already has a 'to'-method. It will NOT " diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions/version.py new/transitions-0.8.10/transitions/version.py --- old/transitions-0.8.9/transitions/version.py 2021-06-28 16:35:09.000000000 +0200 +++ new/transitions-0.8.10/transitions/version.py 2021-10-04 17:27:15.000000000 +0200 @@ -1,5 +1,5 @@ """ Contains the current version of transition which is used in setup.py and can also be used - to determine transitions's version during runtime. + to determine transitions' version during runtime. """ -__version__ = '0.8.9' +__version__ = '0.8.10' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transitions-0.8.9/transitions.egg-info/PKG-INFO new/transitions-0.8.10/transitions.egg-info/PKG-INFO --- old/transitions-0.8.9/transitions.egg-info/PKG-INFO 2021-09-02 12:42:46.000000000 +0200 +++ new/transitions-0.8.10/transitions.egg-info/PKG-INFO 2021-10-04 17:28:22.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: transitions -Version: 0.8.9 +Version: 0.8.10 Summary: A lightweight, object-oriented Python state machine implementation with many extensions. Home-page: http://github.com/pytransitions/transitions Author: Tal Yarkoni @@ -8,7 +8,7 @@ Maintainer: Alexander Neumann Maintainer-email: alen...@gmail.com License: MIT -Download-URL: https://github.com/pytransitions/transitions/archive/0.8.9.tar.gz +Download-URL: https://github.com/pytransitions/transitions/archive/0.8.10.tar.gz Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 @@ -959,7 +959,7 @@ Here you get to consolidate all state machine functionality into your existing model, which often feels more natural way than sticking all of the functionality we want in a separate standalone `Machine` instance. A machine can handle multiple models which can be passed as a list like `Machine(model=[model1, model2, ...])`. -In cases where you want to add models *as well as* the machine instance itself, you can pass the string placeholder `'self'` during initialization like `Machine(model=['self', model1, ...])`. +In cases where you want to add models *as well as* the machine instance itself, you can pass the class variable placeholder (string) `Machine.self_literal` during initialization like `Machine(model=[Machine.self_literal, model1, ...])`. You can also create a standalone machine, and register models dynamically via `machine.add_model` by passing `model=None` to the constructor. Furthermore, you can use `machine.dispatch` to trigger events on all currently added models. Remember to call `machine.remove_model` if machine is long-lasting and your models are temporary and should be garbage collected: