Skip to content

Commit e5b5984

Browse files
authored
Merge branch 'master' into master
2 parents 8a8d949 + 015108c commit e5b5984

17 files changed

+227
-70
lines changed

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
# Changelog
22

3+
## v1.51.2 (2020-03-11)
4+
5+
### Bug Fixes and Other Changes
6+
7+
* handle empty inputs/outputs in ProcessingJob.from_processing_name()
8+
* use DLC images for GovCloud
9+
10+
### Testing and Release Infrastructure
11+
12+
* generate test job name at test start instead of module start
13+
14+
## v1.51.1 (2020-03-10)
15+
16+
### Bug Fixes and Other Changes
17+
18+
* skip pytorch ei test in unsupported regions
19+
20+
### Documentation Changes
21+
22+
* correct MultiString/MULTI_STRING docstring
23+
24+
## v1.51.0 (2020-03-09)
25+
26+
### Features
27+
28+
* pytorch 1.3.1 eia support
29+
30+
### Documentation Changes
31+
32+
* Update Kubernetes Operator default tag
33+
* improve docstring for tuner.best_estimator()
34+
335
## v1.50.18.post0 (2020-03-05)
436

537
### Documentation Changes

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.50.19.dev0
1+
1.51.3.dev0

src/sagemaker/algorithm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,8 @@ def transformer(
328328
instance_type (str): Type of EC2 instance to use, for example,
329329
'ml.c4.xlarge'.
330330
strategy (str): The strategy used to decide how to batch records in
331-
a single request (default: None). Valid values: 'MULTI_RECORD'
332-
and 'SINGLE_RECORD'.
331+
a single request (default: None). Valid values: 'MultiRecord'
332+
and 'SingleRecord'.
333333
assemble_with (str): How the output is assembled (default: None).
334334
Valid values: 'Line' or 'None'.
335335
output_path (str): S3 location for saving the transform result. If

src/sagemaker/estimator.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -823,8 +823,8 @@ def transformer(
823823
instance_type (str): Type of EC2 instance to use, for example,
824824
'ml.c4.xlarge'.
825825
strategy (str): The strategy used to decide how to batch records in
826-
a single request (default: None). Valid values: 'MULTI_RECORD'
827-
and 'SINGLE_RECORD'.
826+
a single request (default: None). Valid values: 'MultiRecord'
827+
and 'SingleRecord'.
828828
assemble_with (str): How the output is assembled (default: None).
829829
Valid values: 'Line' or 'None'.
830830
output_path (str): S3 location for saving the transform result. If
@@ -1895,8 +1895,8 @@ def transformer(
18951895
instance_type (str): Type of EC2 instance to use, for example,
18961896
'ml.c4.xlarge'.
18971897
strategy (str): The strategy used to decide how to batch records in
1898-
a single request (default: None). Valid values: 'MULTI_RECORD'
1899-
and 'SINGLE_RECORD'.
1898+
a single request (default: None). Valid values: 'MultiRecord'
1899+
and 'SingleRecord'.
19001900
assemble_with (str): How the output is assembled (default: None).
19011901
Valid values: 'Line' or 'None'.
19021902
output_path (str): S3 location for saving the transform result. If

src/sagemaker/fw_utils.py

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
]
6363
PY2_RESTRICTED_EIA_FRAMEWORKS = ["pytorch-serving"]
6464
VALID_ACCOUNTS_BY_REGION = {"us-gov-west-1": "246785580436", "us-iso-east-1": "744548109606"}
65-
ASIMOV_VALID_ACCOUNTS_BY_REGION = {"us-iso-east-1": "886529160074"}
65+
ASIMOV_VALID_ACCOUNTS_BY_REGION = {"us-gov-west-1": "442386744353", "us-iso-east-1": "886529160074"}
6666
OPT_IN_ACCOUNTS_BY_REGION = {"ap-east-1": "057415533634", "me-south-1": "724002660598"}
6767
ASIMOV_OPT_IN_ACCOUNTS_BY_REGION = {"ap-east-1": "871362719292", "me-south-1": "217643126080"}
6868
DEFAULT_ACCOUNT = "520713654638"
@@ -133,25 +133,6 @@ def _is_dlc_version(framework, framework_version, py_version):
133133
return False
134134

135135

136-
def _use_dlc_image(region, framework, py_version, framework_version):
137-
"""Return if the DLC image should be used for the given framework,
138-
framework version, Python version, and region.
139-
140-
Args:
141-
region (str): The AWS region.
142-
framework (str): The framework name, e.g. "tensorflow-scriptmode".
143-
py_version (str): The Python version, e.g. "py3".
144-
framework_version (str): The framework version.
145-
146-
Returns:
147-
bool: Whether or not to use the corresponding DLC image.
148-
"""
149-
is_gov_region = region in VALID_ACCOUNTS_BY_REGION
150-
is_dlc_version = _is_dlc_version(framework, framework_version, py_version)
151-
152-
return ((not is_gov_region) or region in ASIMOV_VALID_ACCOUNTS_BY_REGION) and is_dlc_version
153-
154-
155136
def _registry_id(region, framework, py_version, account, framework_version):
156137
"""Return the Amazon ECR registry number (or AWS account ID) for
157138
the given framework, framework version, Python version, and region.
@@ -168,7 +149,7 @@ def _registry_id(region, framework, py_version, account, framework_version):
168149
specific one for the framework, framework version, Python version,
169150
and region, then ``account`` is returned.
170151
"""
171-
if _use_dlc_image(region, framework, py_version, framework_version):
152+
if _is_dlc_version(framework, framework_version, py_version):
172153
if region in ASIMOV_OPT_IN_ACCOUNTS_BY_REGION:
173154
return ASIMOV_OPT_IN_ACCOUNTS_BY_REGION.get(region)
174155
if region in ASIMOV_VALID_ACCOUNTS_BY_REGION:
@@ -253,7 +234,7 @@ def create_image_uri(
253234
else:
254235
device_type = "cpu"
255236

256-
use_dlc_image = _use_dlc_image(region, framework, py_version, framework_version)
237+
use_dlc_image = _is_dlc_version(framework, framework_version, py_version)
257238

258239
if not py_version or (use_dlc_image and framework == "tensorflow-serving-eia"):
259240
tag = "{}-{}".format(framework_version, device_type)

src/sagemaker/model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,8 @@ def transformer(
504504
instance_type (str): Type of EC2 instance to use, for example,
505505
'ml.c4.xlarge'.
506506
strategy (str): The strategy used to decide how to batch records in
507-
a single request (default: None). Valid values: 'MULTI_RECORD'
508-
and 'SINGLE_RECORD'.
507+
a single request (default: None). Valid values: 'MultiRecord'
508+
and 'SingleRecord'.
509509
assemble_with (str): How the output is assembled (default: None).
510510
Valid values: 'Line' or 'None'.
511511
output_path (str): S3 location for saving the transform result. If

src/sagemaker/pipeline.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,8 @@ def transformer(
215215
instance_type (str): Type of EC2 instance to use, for example,
216216
'ml.c4.xlarge'.
217217
strategy (str): The strategy used to decide how to batch records in
218-
a single request (default: None). Valid values: 'MULTI_RECORD'
219-
and 'SINGLE_RECORD'.
218+
a single request (default: None). Valid values: 'MultiRecord'
219+
and 'SingleRecord'.
220220
assemble_with (str): How the output is assembled (default: None).
221221
Valid values: 'Line' or 'None'.
222222
output_path (str): S3 location for saving the transform result. If

src/sagemaker/processing.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -648,10 +648,9 @@ def from_processing_name(cls, sagemaker_session, processing_job_name):
648648
"""
649649
job_desc = sagemaker_session.describe_processing_job(job_name=processing_job_name)
650650

651-
return cls(
652-
sagemaker_session=sagemaker_session,
653-
job_name=processing_job_name,
654-
inputs=[
651+
inputs = None
652+
if job_desc.get("ProcessingInputs"):
653+
inputs = [
655654
ProcessingInput(
656655
source=processing_input["S3Input"]["S3Uri"],
657656
destination=processing_input["S3Input"]["LocalPath"],
@@ -664,19 +663,31 @@ def from_processing_name(cls, sagemaker_session, processing_job_name):
664663
s3_compression_type=processing_input["S3Input"].get("S3CompressionType"),
665664
)
666665
for processing_input in job_desc["ProcessingInputs"]
667-
],
668-
outputs=[
666+
]
667+
668+
outputs = None
669+
if job_desc.get("ProcessingOutputConfig") and job_desc["ProcessingOutputConfig"].get(
670+
"Outputs"
671+
):
672+
outputs = [
669673
ProcessingOutput(
670-
source=job_desc["ProcessingOutputConfig"]["Outputs"][0]["S3Output"][
671-
"LocalPath"
672-
],
673-
destination=job_desc["ProcessingOutputConfig"]["Outputs"][0]["S3Output"][
674-
"S3Uri"
675-
],
676-
output_name=job_desc["ProcessingOutputConfig"]["Outputs"][0]["OutputName"],
674+
source=processing_output["S3Output"]["LocalPath"],
675+
destination=processing_output["S3Output"]["S3Uri"],
676+
output_name=processing_output["OutputName"],
677677
)
678-
],
679-
output_kms_key=job_desc["ProcessingOutputConfig"].get("KmsKeyId"),
678+
for processing_output in job_desc["ProcessingOutputConfig"]["Outputs"]
679+
]
680+
681+
output_kms_key = None
682+
if job_desc.get("ProcessingOutputConfig"):
683+
output_kms_key = job_desc["ProcessingOutputConfig"].get("KmsKeyId")
684+
685+
return cls(
686+
sagemaker_session=sagemaker_session,
687+
job_name=processing_job_name,
688+
inputs=inputs,
689+
outputs=outputs,
690+
output_kms_key=output_kms_key,
680691
)
681692

682693
@classmethod

src/sagemaker/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1968,7 +1968,7 @@ def transform(
19681968
job_name (str): Name of the transform job being created.
19691969
model_name (str): Name of the SageMaker model being used for the transform job.
19701970
strategy (str): The strategy used to decide how to batch records in a single request.
1971-
Possible values are 'MULTI_RECORD' and 'SINGLE_RECORD'.
1971+
Possible values are 'MultiRecord' and 'SingleRecord'.
19721972
max_concurrent_transforms (int): The maximum number of HTTP requests to be made to
19731973
each individual transform container at one time.
19741974
max_payload (int): Maximum size of the payload in a single HTTP request to the

src/sagemaker/tensorflow/estimator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ def transformer(
772772
instance_count (int): Number of EC2 instances to use.
773773
instance_type (str): Type of EC2 instance to use, for example, 'ml.c4.xlarge'.
774774
strategy (str): The strategy used to decide how to batch records in a single request
775-
(default: None). Valid values: 'MULTI_RECORD' and 'SINGLE_RECORD'.
775+
(default: None). Valid values: 'MultiRecord' and 'SingleRecord'.
776776
assemble_with (str): How the output is assembled (default: None). Valid values: 'Line'
777777
or 'None'.
778778
output_path (str): S3 location for saving the transform result. If not specified,

src/sagemaker/utils.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,9 @@ def _create_or_update_code_dir(
530530
"""
531531
code_dir = os.path.join(model_dir, "code")
532532
if os.path.exists(code_dir):
533-
shutil.rmtree(code_dir, ignore_errors=True)
533+
for filename in os.listdir(code_dir):
534+
if filename.endswith(".py"):
535+
os.remove(os.path.join(code_dir, filename))
534536
if source_directory and source_directory.lower().startswith("s3://"):
535537
local_code_path = os.path.join(tmp, "local_code.tar.gz")
536538
download_file_from_url(source_directory, local_code_path, sagemaker_session)
@@ -539,9 +541,12 @@ def _create_or_update_code_dir(
539541
t.extractall(path=code_dir)
540542

541543
elif source_directory:
544+
if os.path.exists(code_dir):
545+
shutil.rmtree(code_dir)
542546
shutil.copytree(source_directory, code_dir)
543547
else:
544-
os.mkdir(code_dir)
548+
if not os.path.exists(code_dir):
549+
os.mkdir(code_dir)
545550
shutil.copy2(inference_script, code_dir)
546551

547552
for dependency in dependencies:

src/sagemaker/workflow/airflow.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -802,8 +802,8 @@ def transform_config_from_estimator(
802802
model_name (str): model name (default: None). If not specified, one will
803803
be generated.
804804
strategy (str): The strategy used to decide how to batch records in a
805-
single request (default: None). Valid values: 'MULTI_RECORD' and
806-
'SINGLE_RECORD'.
805+
single request (default: None). Valid values: 'MultiRecord' and
806+
'SingleRecord'.
807807
assemble_with (str): How the output is assembled (default: None). Valid
808808
values: 'Line' or 'None'.
809809
output_path (str): S3 location for saving the transform result. If not

tests/integ/test_auto_ml.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from __future__ import absolute_import
1414

1515
import os
16-
import time
1716

1817
import pytest
1918
import tests.integ
@@ -34,7 +33,7 @@
3433
TRAINING_DATA = os.path.join(DATA_DIR, "iris_training.csv")
3534
TEST_DATA = os.path.join(DATA_DIR, "iris_test.csv")
3635
PROBLEM_TYPE = "MultiClassClassification"
37-
JOB_NAME = "auto-ml-{}".format(time.strftime("%y%m%d-%H%M%S"))
36+
BASE_JOB_NAME = "auto-ml"
3837

3938
# use a succeeded AutoML job to test describe and list candidates method, otherwise tests will run too long
4039
AUTO_ML_JOB_NAME = "python-sdk-integ-test-base-job"
@@ -119,11 +118,11 @@ def test_auto_ml_fit_optional_args(sagemaker_session):
119118
)
120119
inputs = TRAINING_DATA
121120
with timeout(minutes=AUTO_ML_DEFAULT_TIMEMOUT_MINUTES):
122-
auto_ml.fit(inputs, job_name=JOB_NAME)
121+
auto_ml.fit(inputs, job_name=unique_name_from_base(BASE_JOB_NAME))
123122

124-
auto_ml_desc = auto_ml.describe_auto_ml_job(job_name=JOB_NAME)
123+
auto_ml_desc = auto_ml.describe_auto_ml_job(job_name=auto_ml.latest_auto_ml_job.job_name)
125124
assert auto_ml_desc["AutoMLJobStatus"] == "Completed"
126-
assert auto_ml_desc["AutoMLJobName"] == JOB_NAME
125+
assert auto_ml_desc["AutoMLJobName"] == auto_ml.latest_auto_ml_job.job_name
127126
assert auto_ml_desc["AutoMLJobObjective"] == job_objective
128127
assert auto_ml_desc["ProblemType"] == problem_type
129128
assert auto_ml_desc["OutputDataConfig"]["S3OutputPath"] == output_path

tests/integ/test_processing.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@
2020
from sagemaker import Session
2121
from sagemaker.fw_registry import default_framework_uri
2222

23-
from sagemaker.processing import ProcessingInput, ProcessingOutput, ScriptProcessor, Processor
23+
from sagemaker.processing import (
24+
ProcessingInput,
25+
ProcessingOutput,
26+
ScriptProcessor,
27+
Processor,
28+
ProcessingJob,
29+
)
2430
from sagemaker.sklearn.processing import SKLearnProcessor
2531
from sagemaker.utils import sts_regional_endpoint
2632
from tests.integ import DATA_DIR
@@ -475,6 +481,37 @@ def test_script_processor_with_no_inputs_or_outputs(
475481

476482
assert job_description["StoppingCondition"] == {"MaxRuntimeInSeconds": 3600}
477483

484+
job_from_name = ProcessingJob.from_processing_name(
485+
sagemaker_session=sagemaker_session,
486+
processing_job_name=job_description["ProcessingJobName"],
487+
)
488+
job_description = job_from_name.describe()
489+
490+
assert job_description["ProcessingInputs"][0]["InputName"] == "code"
491+
492+
assert job_description["ProcessingJobName"].startswith("test-script-processor-with-no-inputs")
493+
494+
assert job_description["ProcessingJobStatus"] == "Completed"
495+
496+
assert job_description["ProcessingResources"]["ClusterConfig"]["InstanceCount"] == 1
497+
assert (
498+
job_description["ProcessingResources"]["ClusterConfig"]["InstanceType"] == cpu_instance_type
499+
)
500+
assert job_description["ProcessingResources"]["ClusterConfig"]["VolumeSizeInGB"] == 100
501+
502+
assert job_description["AppSpecification"]["ContainerArguments"] == ["-v"]
503+
assert job_description["AppSpecification"]["ContainerEntrypoint"] == [
504+
"python3",
505+
"/opt/ml/processing/input/code/dummy_script.py",
506+
]
507+
assert job_description["AppSpecification"]["ImageUri"] == image_uri
508+
509+
assert job_description["Environment"] == {"DUMMY_ENVIRONMENT_VARIABLE": "dummy-value"}
510+
511+
assert ROLE in job_description["RoleArn"]
512+
513+
assert job_description["StoppingCondition"] == {"MaxRuntimeInSeconds": 3600}
514+
478515

479516
@pytest.mark.canary_quick
480517
def test_processor(sagemaker_session, image_uri, cpu_instance_type, output_kms_key):

tests/integ/test_pytorch_train.py

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

15-
import os
16-
1715
import numpy
16+
import os
1817
import pytest
19-
from tests.integ import DATA_DIR, PYTHON_VERSION, TRAINING_DEFAULT_TIMEOUT_MINUTES
20-
from tests.integ.timeout import timeout, timeout_and_delete_endpoint_by_name
21-
18+
from sagemaker.pytorch.defaults import LATEST_PY2_VERSION
2219
from sagemaker.pytorch.estimator import PyTorch
2320
from sagemaker.pytorch.model import PyTorchModel
24-
from sagemaker.pytorch.defaults import LATEST_PY2_VERSION
2521
from sagemaker.utils import sagemaker_timestamp
2622

23+
from tests.integ import (
24+
test_region,
25+
DATA_DIR,
26+
PYTHON_VERSION,
27+
TRAINING_DEFAULT_TIMEOUT_MINUTES,
28+
EI_SUPPORTED_REGIONS,
29+
)
30+
from tests.integ.timeout import timeout, timeout_and_delete_endpoint_by_name
31+
2732
MNIST_DIR = os.path.join(DATA_DIR, "pytorch_mnist")
2833
MNIST_SCRIPT = os.path.join(MNIST_DIR, "mnist.py")
2934

@@ -120,6 +125,9 @@ def test_deploy_model(pytorch_training_job, sagemaker_session, cpu_instance_type
120125

121126

122127
@pytest.mark.skipif(PYTHON_VERSION == "py2", reason="PyTorch EIA does not support Python 2.")
128+
@pytest.mark.skipif(
129+
test_region() not in EI_SUPPORTED_REGIONS, reason="EI isn't supported in that specific region."
130+
)
123131
def test_deploy_model_with_accelerator(sagemaker_session, cpu_instance_type):
124132
endpoint_name = "test-pytorch-deploy-eia-{}".format(sagemaker_timestamp())
125133
model_data = sagemaker_session.upload_data(path=EIA_MODEL)
@@ -134,7 +142,7 @@ def test_deploy_model_with_accelerator(sagemaker_session, cpu_instance_type):
134142
predictor = pytorch.deploy(
135143
initial_instance_count=1,
136144
instance_type=cpu_instance_type,
137-
accelerator_type="ml.eia2.medium",
145+
accelerator_type="ml.eia1.medium",
138146
endpoint_name=endpoint_name,
139147
)
140148

0 commit comments

Comments
 (0)