Skip to content

Commit 39df103

Browse files
committed
applied changes of #473 to auto transitions as well
1 parent 38d7dec commit 39df103

File tree

4 files changed

+55
-19
lines changed

4 files changed

+55
-19
lines changed

Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Release 0.8.3 is a minor release and contains several bugfixes mostly related to `HierarchicalStateMachine`:
66

77
- Feature #473: Assign `is_<model_attribute>_<state_name>` instead of `is_<state_name>` when `model_attribute != "state"` to enable multiple versions of such convenience functions. A warning will be raised when `is_<state_name>` is used. (thanks @artofhuman)
8+
- Similarly, auto transitions (`to_<state_name>`) will be assigned as `to_<model_attribute>_<state_name>`. `to_<state_name>` will work as before but raise a warning until version 0.9.0.
89
- Bugfix: `allow_substates` did not consider enum states
910
- Feature: Nested enums can now be passed in a dict as `children` with `initial` parameter
1011
- Bugfix #449: get_triggers/get_transitions did not return nested triggers correctly (thanks @alexandretanem)

README.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ machine.get_state(lump.state).name
347347
>>> 'solid'
348348
```
349349

350-
If you'd like you can choose your own state attribute name by passing the `model_attribute` argument while initializing the `Machine`. This will also change the name of `is_«state name»()` to `is_«model_attribute»_«state name»()` though. This is done to allow multiple machines to work on the same model with individual state attribute names.
350+
If you'd like you can choose your own state attribute name by passing the `model_attribute` argument while initializing the `Machine`. This will also change the name of `is_«state name»()` to `is_«model_attribute»_«state name»()` though. Similarly, auto transitions will be named `to_«model_attribute»_«state name»()` instead of `to_«state name»()`. This is done to allow multiple machines to work on the same model with individual state attribute names.
351351

352352
```python
353353
lump = Matter()
@@ -357,6 +357,8 @@ lump.matter_state
357357
# with a custom 'model_attribute', states can also be checked like this:
358358
lump.is_matter_state_solid()
359359
>>> True
360+
lump.to_matter_state_gas()
361+
>>> True
360362
```
361363

362364
#### <a name="enum-state"></a>Enumerations
@@ -966,7 +968,7 @@ machine.add_model(Matter())
966968
machine.add_model(Matter(), initial='liquid')
967969
```
968970

969-
Models with multiple states could attach multiple machines using different `model_attribute` values. As mentioned in [Checking state](#checking-state), this will add custom `is_<model_attribute>_<state_name>` functions:
971+
Models with multiple states could attach multiple machines using different `model_attribute` values. As mentioned in [Checking state](#checking-state), this will add custom `is/to_<model_attribute>_<state_name>` functions:
970972

971973
```python
972974
lump = Matter()
@@ -983,6 +985,10 @@ lump.shipping_state
983985
>>> 'delivered'
984986
lump.is_shipping_state_delivered() # check the custom field.
985987
>>> True
988+
lump.to_shipping_state_shipping()
989+
>>> True
990+
lump.is_shipping_state_delivered()
991+
>>> False
986992
```
987993

988994
### Logging

tests/test_core.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,8 @@ def __init__(self):
10971097
assert instance.car_state == 'B'
10981098
assert instance.is_driver_state_A()
10991099
assert not instance.is_driver_state_B()
1100+
assert instance.to_driver_state_B()
1101+
assert instance.driver_state == 'B'
11001102

11011103
def test_initial_not_registered(self):
11021104
m1 = self.machine_cls(states=['A', 'B'], initial=self.machine_cls.state_cls('C'))
@@ -1125,7 +1127,12 @@ def __init__(self):
11251127
machine_a = Machine(instance, states=['A', 'B'], initial='A', model_attribute='car_state')
11261128
machine_b = Machine(instance, states=['A', 'B'], initial='B', model_attribute='driver_state')
11271129
self.assertEqual(0, len(w))
1128-
self.assertEqual(True, instance.is_A())
1129-
self.assertEqual(True, instance.is_A())
1130+
self.assertTrue(instance.is_A())
1131+
self.assertTrue(instance.is_A())
11301132
self.assertEqual(1, len(w))
11311133
self.assertEqual(w[0].category, DeprecationWarning)
1134+
instance.to_B()
1135+
self.assertEqual('B', instance.car_state)
1136+
self.assertFalse(instance.is_A())
1137+
self.assertEqual(2, len(w))
1138+
self.assertEqual(w[1].category, DeprecationWarning)

transitions/core.py

+37-15
Original file line numberDiff line numberDiff line change
@@ -784,10 +784,23 @@ def add_states(self, states, on_enter=None, on_exit=None,
784784
for a_state in self.states.keys():
785785
# add all states as sources to auto transitions 'to_<state>' with dest <state>
786786
if a_state == state.name:
787-
self.add_transition('to_%s' % a_state, self.wildcard_all, a_state)
787+
if self.model_attribute == 'state':
788+
method_name = 'to_%s' % a_state
789+
else:
790+
method_name = 'to_%s_%s' % (self.model_attribute, a_state)
791+
self.add_transition('to_%s' % a_state, self.wildcard_all, a_state,
792+
prepare=partial(_warning_wrapper_to, 'to_%s' % a_state))
793+
self.add_transition(method_name, self.wildcard_all, a_state)
794+
788795
# add auto transition with source <state> to <a_state>
789796
else:
790-
self.add_transition('to_%s' % a_state, state.name, a_state)
797+
if self.model_attribute == 'state':
798+
method_name = 'to_%s' % a_state
799+
else:
800+
method_name = 'to_%s_%s' % (self.model_attribute, a_state)
801+
self.add_transition('to_%s' % a_state, state.name, a_state,
802+
prepare=partial(_warning_wrapper_to, 'to_%s' % a_state))
803+
self.add_transition(method_name, state.name, a_state)
791804

792805
def _add_model_to_state(self, state, model):
793806
# Add convenience function 'is_<state_name>' (e.g. 'is_A') to the model.
@@ -796,20 +809,11 @@ def _add_model_to_state(self, state, model):
796809

797810
func = partial(self.is_state, state.value, model)
798811
if self.model_attribute == 'state':
799-
meth_name = 'is_%s' % state.name
812+
method_name = 'is_%s' % state.name
800813
else:
801-
meth_name = 'is_%s_%s' % (self.model_attribute, state.name)
802-
803-
# TODO: Remove in 0.9.0
804-
def _warning_wrapper(*args, **kwargs):
805-
warnings.warn("Starting from transitions version 0.8.3, 'is_<state_name>' convenience functions will be"
806-
" assigned to 'is_<model_attribute>_<state_name>' when 'model_attribute "
807-
"!= \"state\"'. In 0.9.0, 'is_<state_name>' will NOT be assigned anymore when "
808-
"'model_attribute != \"state\"'! Please adjust your code and use "
809-
"'{0}' instead.".format(meth_name), DeprecationWarning)
810-
return func(*args, **kwargs)
811-
self._checked_assignment(model, 'is_%s' % state.name, _warning_wrapper)
812-
self._checked_assignment(model, meth_name, func)
814+
method_name = 'is_%s_%s' % (self.model_attribute, state.name)
815+
self._checked_assignment(model, 'is_%s' % state.name, partial(_warning_wrapper_is, method_name, func))
816+
self._checked_assignment(model, method_name, func)
813817

814818
# Add dynamic method callbacks (enter/exit) if there are existing bound methods in the model
815819
# except if they are already mentioned in 'on_enter/exit' of the defined state
@@ -1215,3 +1219,21 @@ def __init__(self, value):
12151219

12161220
def __str__(self):
12171221
return repr(self.value)
1222+
1223+
1224+
# TODO: Remove in 0.9.0
1225+
def _warning_wrapper_is(meth_name, func, *args, **kwargs):
1226+
warnings.warn("Starting from transitions version 0.8.3, 'is_<state_name>' convenience functions will be"
1227+
" assigned to 'is_<model_attribute>_<state_name>' when 'model_attribute "
1228+
"!= \"state\"'. In 0.9.0, 'is_<state_name>' will NOT be assigned anymore when "
1229+
"'model_attribute != \"state\"'! Please adjust your code and use "
1230+
"'{0}' instead.".format(meth_name), DeprecationWarning)
1231+
return func(*args, **kwargs)
1232+
1233+
1234+
def _warning_wrapper_to(meth_name, *args, **kwargs):
1235+
warnings.warn("Starting from transitions version 0.8.3, 'to_<state_name>' convenience functions will be"
1236+
" assigned to 'to_<model_attribute>_<state_name>' when 'model_attribute "
1237+
"!= \"state\"'. In 0.9.0, 'to_<state_name>' will NOT be assigned anymore when "
1238+
"'model_attribute != \"state\"'! Please adjust your code and use "
1239+
"'{0}' instead.".format(meth_name), DeprecationWarning)

0 commit comments

Comments
 (0)