Skip to content

Commit 08b6fde

Browse files
authored
fix: Default AsyncPredictor upload prefix to endpoint name (#6337)
AsyncPredictor accepts name=None by default, but predict() and predict_async() called name_from_base(self.name) when uploading input data without an input_path, which raised "TypeError: 'NoneType' object is not subscriptable". Fall back to the wrapped predictor's endpoint name when no name is given, and document the name argument. An explicitly provided name is still used as the S3 key prefix. Fixes #3210 Fixes #4774
1 parent f25e36f commit 08b6fde

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎src/sagemaker/predictor_async.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def __init__(
4040
predictor (sagemaker.predictor.Predictor): General ``Predictor``
4141
object has useful methods and variables. ``AsyncPredictor``
4242
stands on top of it with capability for async inference.
43+
name (str): Optional. Name used as the prefix of the Amazon S3 key when
44+
input data is uploaded for async inference. If not specified, the
45+
endpoint name is used. (Default: None)
4346
"""
4447
warn_v2_deprecation(
4548
feature="AsyncPredictor",
@@ -200,10 +203,13 @@ def _upload_data_to_s3(
200203
my_uuid = str(uuid.uuid4())
201204
timestamp = sagemaker_timestamp()
202205
bucket = self.sagemaker_session.default_bucket()
206+
# ``name`` is optional; fall back to the endpoint name so the default
207+
# ``AsyncPredictor(predictor)`` construction can upload input data.
208+
base_name = self.name or self.endpoint_name
203209
key = s3.s3_path_join(
204210
self.sagemaker_session.default_bucket_prefix,
205211
"async-endpoint-inputs",
206-
name_from_base(self.name, short=True),
212+
name_from_base(base_name, short=True),
207213
"{}-{}".format(timestamp, my_uuid),
208214
)
209215

‎tests/unit/test_predictor_async.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,31 @@ def test_async_predict_call_with_data():
174174
assert result.output_path == ASYNC_OUTPUT_LOCATION
175175

176176

177+
def test_async_predict_call_with_data_and_no_name():
178+
# Regression test for https://github.com/aws/sagemaker-python-sdk/issues/3210
179+
sagemaker_session = empty_sagemaker_session()
180+
predictor_async = AsyncPredictor(Predictor(ENDPOINT, sagemaker_session))
181+
assert predictor_async.name is None
182+
183+
result = predictor_async.predict_async(data=DUMMY_DATA)
184+
185+
_, put_kwargs = sagemaker_session.s3_client.put_object.call_args
186+
assert put_kwargs["Bucket"] == BUCKET_NAME
187+
assert put_kwargs["Key"].startswith("async-endpoint-inputs/{}-".format(ENDPOINT))
188+
assert predictor_async._input_path == "s3://{}/{}".format(BUCKET_NAME, put_kwargs["Key"])
189+
assert result.output_path == ASYNC_OUTPUT_LOCATION
190+
191+
192+
def test_async_predict_call_with_data_and_name_uses_name():
193+
sagemaker_session = empty_sagemaker_session()
194+
predictor_async = AsyncPredictor(Predictor(ENDPOINT, sagemaker_session), name=ASYNC_PREDICTOR)
195+
196+
predictor_async.predict_async(data=DUMMY_DATA)
197+
198+
_, put_kwargs = sagemaker_session.s3_client.put_object.call_args
199+
assert put_kwargs["Key"].startswith("async-endpoint-inputs/{}-".format(ASYNC_PREDICTOR))
200+
201+
177202
def test_async_predict_call_with_data_and_input_path():
178203
sagemaker_session = empty_sagemaker_session()
179204
predictor_async = AsyncPredictor(Predictor(ENDPOINT, sagemaker_session))

0 commit comments

Comments
 (0)