Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Choice states chainable #132

Merged
merged 9 commits into from
May 25, 2021
Merged

Conversation

ca-nguyen
Copy link
Contributor

@ca-nguyen ca-nguyen commented May 13, 2021

Issue #, if available:31

Description of changes:
It was discussed in issue #31 that the non terminal Choice state behaviour was not obvious since it could not be chained. This PR allows Choice states to be chained by setting default_choice() instead of the Next field (that the Choice state does not have).

Test
Added 2 unit tests in test_steps to test:

  1. Choice state can be chained: No error thrown and default is set.
  2. WARNING is logged when the chained Choice state already has a default as chaining it will overwrite it

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Copy link
Contributor

@shivlaks shivlaks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description of changes in the commit summary should capture the rationale behind the change in addition to what is being changed.

Comment on lines 385 to 394
# Chain s2_choice when default_choice is already set will trigger Warning
with caplog.at_level(logging.WARNING):
Chain([s2_choice, s1_pass])
log_message = (
"Chaining Choice Step: Overwriting %s's current default_choice (%s) with %s" %
(s2_choice.state_id, s3_pass.state_id, s1_pass.state_id)
)
assert log_message in caplog.text
assert s2_choice.default == s1_pass
assert s2_choice.next_step is None # Choice steps do not have next_step
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure whether this is an idiomatic Python thing, but I'd extract this into a separate test:

the way I interpret the control flow, there are 2 scenarios and sets of assertions.

The 2 tests from what i can tell:

  1. test that choice state has a default that is established if there wasn't one because it MUST transition to another state
  2. test that warning is produced when a default state is already set and a choice state is chained because the behaviour is counter-intuitive + overwrites an existing value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it makes sense to divide the test in 2!

Comment on lines 389 to 390
"Chaining Choice Step: Overwriting %s's current default_choice (%s) with %s" %
(s2_choice.state_id, s3_pass.state_id, s1_pass.state_id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: might be more clear to just establish the expected string and call it expected_warning or something similar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Made the changes in latest commit

"Chaining Choice Step: Overwriting %s's current default_choice (%s) with %s" %
(s2_choice.state_id, s3_pass.state_id, s1_pass.state_id)
)
assert log_message in caplog.text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the log include other stuff? curious why we use in and not equality?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it includes the logging type (in our case WARNING) and the line(and file) where it occurred.
I can add an assert to check if "WARNING" is in caplog.text

CONTRIBUTING.md Outdated
@@ -80,7 +80,7 @@ You should only worry about manually running any new integration tests that you

1. Create a new git branch:
```shell
git checkout -b my-fix-branch master
git checkout -b my-fix-branch main
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer these in a separate PR to observe our guidance to focus on the change being introduced. this PR is small so it doesn't take away focus, but just something to keep in mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)
Removed the changes into another PR!

if self.type is 'Choice':
if self.default is not None:
logger.warning(
"Chaining Choice Step: Overwriting %s's current default_choice (%s) with %s",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Chaining Choice Step: Overwriting %s's current default_choice (%s) with %s",
"Chaining Choice state: Overwriting %s's current default_choice (%s) with %s",

I think we should be consistent with calling it a state

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Done in the latest commit :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the SDK does refer to "steps" with states being a type of step.

raise ValueError('Unexpected State instance `{step}`, State type `{state_type}` does not support method `next`.'.format(step=next_step, state_type=self.type))

# By design, choice states do not have the Next field. Setting default to make it chainable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explain the why rather than the what so that future contributors can gain insight.

there's some great info in the gist that @wong-a created that could be paraphrased. Might also be helpful to link to the language spec

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!
Done!

@@ -372,6 +367,40 @@ def test_chaining_steps():
assert s1.next_step == s2
assert s2.next_step == s3


def test_chaining_choice():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's helpful to include the unit being tested, the inputs, and the expected outcome in the test names. This acts documentation for the expected behaviour.

Suggested change
def test_chaining_choice():
def test_chaining_choice_sets_default_field():

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Will make the change in the next commit.

assert s1_pass.next_step == s2_choice
assert s2_choice.default == s3_pass
assert s2_choice.next_step is None # Choice steps do not have next_step
assert s3_pass.next_step is None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When writing unit tests for these modules we should consider making assertions on the generated JSON too. One of the main responsibilities is generating ASL so we should make sure it's correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Will include assertions in the generated JSON in the next commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, there is already a unit test (test_choice_state_creation) that ensures the Choice state creation generates the expected ASL

tests/unit/test_steps.py Show resolved Hide resolved
assert s3_pass.next_step is None


def test_chaining_choice_with_default(caplog):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def test_chaining_choice_with_default(caplog):
def test_chaining_choice_with_existing_default_overrides_value(caplog):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, the suggested name makes the test objective clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tip: you can use the Commit suggestion button to commit suggestions in the UI

raise ValueError('Unexpected State instance `{step}`, State type `{state_type}` does not support method `next`.'.format(step=next_step, state_type=self.type))

# By design, Choice states do not have the Next field. Their purpose is to define conditional transitions based
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should document the behaviour of chaining Choice states publicly. Consumers of the SDK won't be looking at the source code. Something along the lines of:

When used in a chain, the subsequent step becomes the default choice that executes if none of the specified rules match.

https://github.com/aws/aws-step-functions-data-science-sdk-python/blob/main/src/stepfunctions/steps/states.py#L402-L470

The docstrings in our source code become part of the public documentation and are visible in user's IDE: https://aws-step-functions-data-science-sdk.readthedocs.io/en/stable/states.html#stepfunctions.steps.states.Choice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point! Will make sure to update the public documentation as well.

@ca-nguyen ca-nguyen requested a review from shivlaks May 14, 2021 23:44
@ca-nguyen ca-nguyen requested a review from wong-a May 15, 2021 00:54
if self.type is 'Choice':
if self.default is not None:
logger.warning(
"Chaining Choice state: Overwriting %s's current default_choice (%s) with %s",
Copy link
Contributor

@yuan-bwn yuan-bwn May 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not a blocker but can try using f-string next time: https://www.python.org/dev/peps/pep-0498/
Got this recommendation from Shiv and think this could also help here. Same for the unit test string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Using fstring will make it more readable and faster! Making changes in the next commit!

wong-a
wong-a previously approved these changes May 19, 2021
shivlaks
shivlaks previously approved these changes May 21, 2021
@ca-nguyen ca-nguyen dismissed stale reviews from shivlaks and wong-a via 2c72de7 May 21, 2021 15:00
shivlaks
shivlaks previously approved these changes May 21, 2021
wong-a
wong-a previously approved these changes May 21, 2021
@StepFunctions-Bot
Copy link
Contributor

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject6AEA49D1-sEHrOdk7acJc
  • Commit ID: aa82ca5
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@ca-nguyen ca-nguyen merged commit 6091932 into aws:main May 25, 2021
DyfanJones pushed a commit to DyfanJones/aws-step-functions-data-science-sdk-r that referenced this pull request May 26, 2021
@wong-a wong-a mentioned this pull request May 27, 2021
wong-a added a commit that referenced this pull request May 27, 2021
Increments VERSION for release to PyPI which will include the following changes since v2.0.0:
* feature: Make Choice states chainable #132
* fix: Make service integration ARNs partition aware #131
@ca-nguyen ca-nguyen deleted the chain-choice-states branch October 27, 2021 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants