Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6091932

Browse files
Merge pull request #132 from ca-nguyen/chain-choice-states
Make Choice states chainable
2 parents 4f90ba3 + aa82ca5 commit 6091932

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

‎src/stepfunctions/steps/states.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,18 @@ def next(self, next_step):
218218
Returns:
219219
State or Chain: Next state or chain that will be transitioned to.
220220
"""
221-
if self.type in ('Choice', 'Succeed', 'Fail'):
221+
if self.type in ('Succeed', 'Fail'):
222222
raise ValueError('Unexpected State instance `{step}`, State type `{state_type}` does not support method `next`.'.format(step=next_step, state_type=self.type))
223223

224+
# By design, Choice states do not have the Next field. When used in a chain, the subsequent step becomes the
225+
# default choice that executes if none of the specified rules match.
226+
# See language spec for more info: https://states-language.net/spec.html#choice-state
227+
if self.type is 'Choice':
228+
if self.default is not None:
229+
logger.warning(f'Chaining Choice state: Overwriting {self.state_id}\'s current default_choice ({self.default.state_id}) with {next_step.state_id}')
230+
self.default_choice(next_step)
231+
return self.default
232+
224233
self.next_step = next_step
225234
return self.next_step
226235

@@ -402,7 +411,7 @@ def allowed_fields(self):
402411
class Choice(State):
403412

404413
"""
405-
Choice state adds branching logic to a state machine. The state holds a list of *rule* and *next_step* pairs. The interpreter attempts pattern-matches against the rules in list order and transitions to the state or chain specified in the *next_step* field on the first *rule* where there is an exact match between the input value and a member of the comparison-operator array.
414+
Choice state adds branching logic to a state machine. The state holds a list of *rule* and *next_step* pairs. The interpreter attempts pattern-matches against the rules in list order and transitions to the state or chain specified in the *next_step* field on the first *rule* where there is an exact match between the input value and a member of the comparison-operator array. When used in a chain, the subsequent step becomes the default choice that executes if none of the specified rules match.
406415
"""
407416

408417
def __init__(self, state_id, **kwargs):

‎tests/unit/test_steps.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# permissions and limitations under the License.
1313
from __future__ import absolute_import
1414

15+
import logging
1516
import pytest
1617

1718
from stepfunctions.exceptions import DuplicateStatesInChain
@@ -346,12 +347,6 @@ def test_append_states_after_terminal_state_will_fail():
346347
chain.append(Succeed('Succeed'))
347348
chain.append(Pass('Pass2'))
348349

349-
with pytest.raises(ValueError):
350-
chain = Chain()
351-
chain.append(Pass('Pass'))
352-
chain.append(Choice('Choice'))
353-
chain.append(Pass('Pass2'))
354-
355350

356351
def test_chaining_steps():
357352
s1 = Pass('Step - One')
@@ -391,6 +386,36 @@ def test_chaining_steps():
391386
assert s2.next_step == s3
392387

393388

389+
def test_chaining_choice_sets_default_field():
390+
s1_pass = Pass('Step - One')
391+
s2_choice = Choice('Step - Two')
392+
s3_pass = Pass('Step - Three')
393+
394+
chain1 = Chain([s1_pass, s2_choice, s3_pass])
395+
assert chain1.steps == [s1_pass, s2_choice, s3_pass]
396+
assert s1_pass.next_step == s2_choice
397+
assert s2_choice.default == s3_pass
398+
assert s2_choice.next_step is None # Choice steps do not have next_step
399+
assert s3_pass.next_step is None
400+
401+
402+
def test_chaining_choice_with_existing_default_overrides_value(caplog):
403+
s1_pass = Pass('Step - One')
404+
s2_choice = Choice('Step - Two')
405+
s3_pass = Pass('Step - Three')
406+
407+
s2_choice.default_choice(s3_pass)
408+
409+
# Chain s2_choice when default_choice is already set will trigger Warning message
410+
with caplog.at_level(logging.WARNING):
411+
Chain([s2_choice, s1_pass])
412+
expected_warning = f'Chaining Choice state: Overwriting {s2_choice.state_id}\'s current default_choice ({s3_pass.state_id}) with {s1_pass.state_id}'
413+
assert expected_warning in caplog.text
414+
assert 'WARNING' in caplog.text
415+
assert s2_choice.default == s1_pass
416+
assert s2_choice.next_step is None # Choice steps do not have next_step
417+
418+
394419
def test_catch_fail_for_unsupported_state():
395420
s1 = Pass('Step - One')
396421

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /