Skip to content

Commit 7ae50d6

Browse files
authored
Merge branch 'main' into training-step-with-dynamic-output-path
2 parents 969a795 + b773fea commit 7ae50d6

10 files changed

+206
-20
lines changed

CONTRIBUTING.md

+42-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ information to effectively respond to your bug report or contribution.
1111

1212
* [Table of Contents](#table-of-contents)
1313
* [Reporting Bugs/Feature Requests](#reporting-bugsfeature-requests)
14-
* [Contributing via Pull Requests (PRs)](#contributing-via-pull-requests-prs)
14+
* [Contributing via Pull Requests (PRs)](#contributing-via-pull-requests-prs)
1515
* [Pulling Down the Code](#pulling-down-the-code)
1616
* [Running the Unit Tests](#running-the-unit-tests)
1717
* [Running the Integration Tests](#running-the-integration-tests)
@@ -61,20 +61,56 @@ Before sending us a pull request, please ensure that:
6161
1. Install test dependencies, including coverage, using `pip install ".[test]"`
6262
1. Run the following tox command and verify that all code checks and unit tests pass: `tox tests/unit`
6363

64-
You can also run a single test with the following command: `tox -e py36 -- -s -vv <path_to_file><file_name>::<test_function_name>`
64+
You can also run a single test with the following command: `tox -e py36 -- -s -vv <path_to_file><file_name>::<test_function_name>`
6565
* Note that the coverage test will fail if you only run a single test, so make sure to surround the command with `export IGNORE_COVERAGE=-` and `unset IGNORE_COVERAGE`
6666
* Example: `export IGNORE_COVERAGE=- ; tox -e py36 -- -s -vv tests/unit/test_sagemaker_steps.py::test_training_step_creation_with_model ; unset IGNORE_COVERAGE`
6767

6868

6969
### Running the Integration Tests
7070

71-
Our CI system runs integration tests (the ones in the `tests/integ` directory), in parallel, for every Pull Request.
72-
You should only worry about manually running any new integration tests that you write, or integration tests that test an area of code that you've modified.
71+
Our CI system runs integration tests (the ones in the `tests/integ` directory), in parallel, for every Pull Request.
72+
You should only worry about manually running any new integration tests that you write, or integration tests that test an area of code that you've modified.
73+
#### Setup
7374

74-
1. Follow the instructions at [Set Up the AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/polly/latest/dg/setup-aws-cli.html).
75+
If you haven't done so already, install tox and test dependencies:
76+
1. `pip install tox`
77+
1. `pip install .[test]`
78+
79+
#### AWS Credentials
80+
Follow the instructions at [Set Up the AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/polly/latest/dg/setup-aws-cli.html).
81+
#### Create IAM Roles
82+
83+
The tests use two IAM roles to give Step Functions and SageMaker permissions to access AWS resources in your account. Use the following commands in the root directory of this repository:
84+
85+
```bash
86+
aws iam create-role \
87+
--role-name StepFunctionsMLWorkflowExecutionFullAccess \
88+
--assume-role-policy-document file://tests/integ/resources/StepFunctionsMLWorkflowExecutionFullAccess-TrustPolicy.json
89+
```
90+
91+
```bash
92+
aws iam put-role-policy \
93+
--role-name StepFunctionsMLWorkflowExecutionFullAccess \
94+
--policy-name StepFunctionsMLWorkflowExecutionFullAccess \
95+
--policy-document file://tests/integ/resources/StepFunctionsMLWorkflowExecutionFullAccess-Policy.json
96+
```
97+
98+
```bash
99+
aws iam create-role \
100+
--role-name SageMakerRole \
101+
--assume-role-policy-document file://tests/integ/resources/SageMaker-TrustPolicy.json
102+
```
103+
104+
```bash
105+
aws iam attach-role-policy \
106+
--role-name SageMakerRole \
107+
--policy-arn arn:aws:iam::aws:policy/AmazonSageMakerFullAccess
108+
```
109+
#### Execute the tests
75110
1. To run a test, specify the test file and method you want to run per the following command: `tox -e py36 -- -s -vv <path_to_file><file_name>::<test_function_name>`
76111
* Note that the coverage test will fail if you only run a single test, so make sure to surround the command with `export IGNORE_COVERAGE=-` and `unset IGNORE_COVERAGE`
77112
* Example: `export IGNORE_COVERAGE=- ; tox -e py36 -- -s -vv tests/integ/test_state_machine_definition.py::test_wait_state_machine_creation ; unset IGNORE_COVERAGE`
113+
1. To run all integration tests, run the following command: `tox tests/integ`
78114

79115
### Making and Testing Your Change
80116

@@ -88,7 +124,7 @@ You should only worry about manually running any new integration tests that you
88124
1. Guard against future breaking changes to lower the maintenance cost.
89125
1. Please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
90126
1. Run all the unit tests as per [Running the Unit Tests](#running-the-unit-tests), and verify that all checks and tests pass.
91-
1. Note that this also runs tools that may be necessary for the automated build to pass (ex: code reformatting by 'black').
127+
1. Note that this also runs tools that may be necessary for the automated build to pass (ex: code reformatting by 'black').
92128

93129

94130
### Committing Your Change

README.rst

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Table of Contents
2727
- `Getting Started With Sample Jupyter Notebooks <#getting-started-with-sample-jupyter-notebooks>`__
2828
- `Installing the AWS Step Functions Data Science SDK <#installing-the-aws-step-functions-data-science-sdk>`__
2929
- `Overview of SDK <#overview-of-sdk>`__
30+
- `Contributing <#contributing>`__
3031
- `AWS Permissions <#aws-permissions>`__
3132
- `Licensing <#licensing>`__
3233
- `Verifying the Signature <#verifying-the-signature>`__
@@ -309,6 +310,12 @@ the CloudFormation template in a different region, please make sure to update
309310
the region specific AWS resources (such as the Lambda ARN and Training Image)
310311
in the StateMachine definition.
311312

313+
Contributing
314+
------------
315+
We welcome community contributions and pull requests. See
316+
`CONTRIBUTING.md <https://github.com/aws/aws-step-functions-data-science-sdk-python/blob/main/CONTRIBUTING.md>`__ for
317+
information on how to set up a development environment, run tests and submit code.
318+
312319
AWS Permissions
313320
---------------
314321
As a managed service, AWS Step Functions performs operations on your behalf on

doc/services.rst

+16-10
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ This module provides classes to build steps that integrate with Amazon DynamoDB,
88

99
- `Amazon DynamoDB <#amazon-dynamodb>`__
1010

11+
- `Amazon EMR <#amazon-emr>`__
12+
13+
- `Amazon EventBridge <#amazon-eventbridge>`__
14+
1115
- `Amazon SNS <#amazon-sns>`__
1216

1317
- `Amazon SQS <#amazon-sqs>`__
1418

15-
- `Amazon EMR <#amazon-emr>`__
16-
1719

1820
Amazon DynamoDB
1921
----------------
@@ -25,14 +27,6 @@ Amazon DynamoDB
2527

2628
.. autoclass:: stepfunctions.steps.service.DynamoDBUpdateItemStep
2729

28-
Amazon SNS
29-
-----------
30-
.. autoclass:: stepfunctions.steps.service.SnsPublishStep
31-
32-
Amazon SQS
33-
-----------
34-
.. autoclass:: stepfunctions.steps.service.SqsSendMessageStep
35-
3630
Amazon EMR
3731
-----------
3832
.. autoclass:: stepfunctions.steps.service.EmrCreateClusterStep
@@ -48,3 +42,15 @@ Amazon EMR
4842
.. autoclass:: stepfunctions.steps.service.EmrModifyInstanceFleetByNameStep
4943

5044
.. autoclass:: stepfunctions.steps.service.EmrModifyInstanceGroupByNameStep
45+
46+
Amazon EventBridge
47+
-----------
48+
.. autoclass:: stepfunctions.steps.service.EventBridgePutEventsStep
49+
50+
Amazon SNS
51+
-----------
52+
.. autoclass:: stepfunctions.steps.service.SnsPublishStep
53+
54+
Amazon SQS
55+
-----------
56+
.. autoclass:: stepfunctions.steps.service.SqsSendMessageStep

src/stepfunctions/steps/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
from stepfunctions.steps.sagemaker import TrainingStep, TransformStep, ModelStep, EndpointConfigStep, EndpointStep, TuningStep, ProcessingStep
2020
from stepfunctions.steps.compute import LambdaStep, BatchSubmitJobStep, GlueStartJobRunStep, EcsRunTaskStep
2121
from stepfunctions.steps.service import DynamoDBGetItemStep, DynamoDBPutItemStep, DynamoDBUpdateItemStep, DynamoDBDeleteItemStep
22-
from stepfunctions.steps.service import SnsPublishStep, SqsSendMessageStep
2322
from stepfunctions.steps.service import EmrCreateClusterStep, EmrTerminateClusterStep, EmrAddStepStep, EmrCancelStepStep, EmrSetClusterTerminationProtectionStep, EmrModifyInstanceFleetByNameStep, EmrModifyInstanceGroupByNameStep
24-
23+
from stepfunctions.steps.service import EventBridgePutEventsStep
24+
from stepfunctions.steps.service import SnsPublishStep, SqsSendMessageStep

src/stepfunctions/steps/service.py

+47-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
from stepfunctions.steps.integration_resources import IntegrationPattern, get_service_integration_arn
1919

2020
DYNAMODB_SERVICE_NAME = "dynamodb"
21+
ELASTICMAPREDUCE_SERVICE_NAME = "elasticmapreduce"
22+
EVENTBRIDGE_SERVICE_NAME = "events"
2123
SNS_SERVICE_NAME = "sns"
2224
SQS_SERVICE_NAME = "sqs"
23-
ELASTICMAPREDUCE_SERVICE_NAME = "elasticmapreduce"
25+
2426

2527

2628
class DynamoDBApi(Enum):
@@ -48,6 +50,10 @@ class ElasticMapReduceApi(Enum):
4850
ModifyInstanceGroupByName = "modifyInstanceGroupByName"
4951

5052

53+
class EventBridgeApi(Enum):
54+
PutEvents = "putEvents"
55+
56+
5157
class DynamoDBGetItemStep(Task):
5258
"""
5359
Creates a Task state to get an item from DynamoDB. See `Call DynamoDB APIs with Step Functions <https://docs.aws.amazon.com/step-functions/latest/dg/connect-ddb.html>`_ for more details.
@@ -77,6 +83,46 @@ def __init__(self, state_id, **kwargs):
7783
super(DynamoDBGetItemStep, self).__init__(state_id, **kwargs)
7884

7985

86+
class EventBridgePutEventsStep(Task):
87+
88+
"""
89+
Creates a Task to send custom events to Amazon EventBridge. See`Call EventBridge with Step Functions <https://docs.aws.amazon.com/step-functions/latest/dg/connect-eventbridge.html>`_ for more details.
90+
"""
91+
92+
def __init__(self, state_id, wait_for_callback=False, **kwargs):
93+
"""
94+
Args:
95+
state_id (str): State name whose length **must be** less than or equal to 128 unicode characters. State names **must be** unique within the scope of the whole state machine.
96+
comment (str, optional): Human-readable comment or description. (default: None)
97+
timeout_seconds (int, optional): Positive integer specifying timeout for the state in seconds. If the state runs longer than the specified timeout, then the interpreter fails the state with a `States.Timeout` Error Name. (default: 60)
98+
timeout_seconds_path (str, optional): Path specifying the state's timeout value in seconds from the state input. When resolved, the path must select a field whose value is a positive integer.
99+
heartbeat_seconds (int, optional): Positive integer specifying heartbeat timeout for the state in seconds. This value should be lower than the one specified for `timeout_seconds`. If more time than the specified heartbeat elapses between heartbeats from the task, then the interpreter fails the state with a `States.Timeout` Error Name.
100+
heartbeat_seconds_path (str, optional): Path specifying the state's heartbeat value in seconds from the state input. When resolved, the path must select a field whose value is a positive integer.
101+
input_path (str, optional): Path applied to the state’s raw input to select some or all of it; that selection is used by the state. (default: '$')
102+
parameters (dict, optional): The value of this field becomes the effective input for the state.
103+
result_path (str, optional): Path specifying the raw input’s combination with or replacement by the state’s result. (default: '$')
104+
output_path (str, optional): Path applied to the state’s output after the application of `result_path`, producing the effective output which serves as the raw input for the next state. (default: '$')
105+
"""
106+
107+
if wait_for_callback:
108+
"""
109+
Example resource arn: arn:aws:states:::events:putEvents.waitForTaskToken
110+
"""
111+
112+
kwargs[Field.Resource.value] = get_service_integration_arn(EVENTBRIDGE_SERVICE_NAME,
113+
EventBridgeApi.PutEvents,
114+
IntegrationPattern.WaitForTaskToken)
115+
else:
116+
"""
117+
Example resource arn: arn:aws:states:::events:putEvents
118+
"""
119+
120+
kwargs[Field.Resource.value] = get_service_integration_arn(EVENTBRIDGE_SERVICE_NAME,
121+
EventBridgeApi.PutEvents)
122+
123+
super(EventBridgePutEventsStep, self).__init__(state_id, **kwargs)
124+
125+
80126
class DynamoDBPutItemStep(Task):
81127

82128
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"Version": "2012-10-17",
3+
"Statement": [
4+
{
5+
"Sid": "",
6+
"Effect": "Allow",
7+
"Principal": {
8+
"Service": "sagemaker.amazonaws.com"
9+
},
10+
"Action": "sts:AssumeRole"
11+
}
12+
]
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"Version": "2012-10-17",
3+
"Statement": [
4+
{
5+
"Sid": "",
6+
"Effect": "Allow",
7+
"Principal": {
8+
"Service": "states.amazonaws.com"
9+
},
10+
"Action": "sts:AssumeRole"
11+
}
12+
]
13+
}

tests/unit/test_service_steps.py

+65
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from stepfunctions.steps.service import DynamoDBGetItemStep, DynamoDBPutItemStep, DynamoDBUpdateItemStep, DynamoDBDeleteItemStep
2020
from stepfunctions.steps.service import SnsPublishStep, SqsSendMessageStep
2121
from stepfunctions.steps.service import EmrCreateClusterStep, EmrTerminateClusterStep, EmrAddStepStep, EmrCancelStepStep, EmrSetClusterTerminationProtectionStep, EmrModifyInstanceFleetByNameStep, EmrModifyInstanceGroupByNameStep
22+
from stepfunctions.steps.service import EventBridgePutEventsStep
2223

2324

2425
@patch.object(boto3.session.Session, 'region_name', 'us-east-1')
@@ -98,6 +99,70 @@ def test_sqs_send_message_step_creation():
9899
'End': True
99100
}
100101

102+
@patch.object(boto3.session.Session, 'region_name', 'us-east-1')
103+
def test_eventbridge_put_events_step_creation():
104+
step = EventBridgePutEventsStep('Send to EventBridge', parameters={
105+
"Entries": [
106+
{
107+
"Detail": {
108+
"Message": "MyMessage"
109+
},
110+
"DetailType": "MyDetailType",
111+
"EventBusName": "MyEventBus",
112+
"Source": "my.source"
113+
}
114+
]
115+
})
116+
117+
assert step.to_dict() == {
118+
"Type": "Task",
119+
"Resource": 'arn:aws:states:::events:putEvents',
120+
"Parameters": {
121+
"Entries": [
122+
{
123+
"Detail": {
124+
"Message": "MyMessage"
125+
},
126+
"DetailType": "MyDetailType",
127+
"EventBusName": "MyEventBus",
128+
"Source": "my.source"
129+
}
130+
]
131+
},
132+
"End": True
133+
}
134+
135+
step = EventBridgePutEventsStep('Send to EventBridge', wait_for_callback=True, parameters={
136+
"Entries": [
137+
{
138+
"Detail": {
139+
"Message.$": "$.MyMessage"
140+
},
141+
"DetailType": "MyDetailType",
142+
"EventBusName": "MyEventBus",
143+
"Source": "my.source"
144+
}
145+
]
146+
})
147+
148+
assert step.to_dict() == {
149+
"Type": "Task",
150+
"Resource": "arn:aws:states:::events:putEvents.waitForTaskToken",
151+
"Parameters": {
152+
"Entries": [
153+
{
154+
"Detail": {
155+
"Message.$": "$.MyMessage"
156+
},
157+
"DetailType": "MyDetailType",
158+
"EventBusName": "MyEventBus",
159+
"Source": "my.source"
160+
}
161+
]
162+
},
163+
"End": True
164+
}
165+
101166

102167
@patch.object(boto3.session.Session, 'region_name', 'us-east-1')
103168
def test_dynamodb_get_item_step_creation():

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ envlist = python3.6
99
skip_missing_interpreters = False
1010

1111
[testenv]
12-
install_command = pip install {opts} {packages} {env:PWD}[test]
12+
deps = .[test]
1313
passenv =
1414
AWS_ACCESS_KEY_ID
1515
AWS_SECRET_ACCESS_KEY

0 commit comments

Comments
 (0)