Skip to content

Commit eb186cc

Browse files
authored
Merge branch 'master' into MLX-1269
2 parents 84ef4f3 + 0075fb3 commit eb186cc

File tree

6 files changed

+80
-138
lines changed

6 files changed

+80
-138
lines changed

CHANGELOG.md

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

3+
## v2.218.1 (2024-05-03)
4+
5+
### Bug Fixes and Other Changes
6+
7+
* Fix UserAgent logging in Python SDK
8+
* chore: release tgi 2.0.1
9+
* chore: update skipped flaky tests
10+
311
## v2.218.0 (2024-05-01)
412

513
### Features

VERSION

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

src/sagemaker/session.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
from sagemaker.deprecations import deprecated_class
122122
from sagemaker.enums import EndpointType
123123
from sagemaker.inputs import ShuffleConfig, TrainingInput, BatchDataCaptureConfig
124-
from sagemaker.user_agent import prepend_user_agent
124+
from sagemaker.user_agent import get_user_agent_extra_suffix
125125
from sagemaker.utils import (
126126
name_from_image,
127127
secondary_training_status_changed,
@@ -285,6 +285,7 @@ def _initialize(
285285
Creates or uses a boto_session, sagemaker_client and sagemaker_runtime_client.
286286
Sets the region_name.
287287
"""
288+
288289
self.boto_session = boto_session or boto3.DEFAULT_SESSION or boto3.Session()
289290

290291
self._region_name = self.boto_session.region_name
@@ -293,19 +294,30 @@ def _initialize(
293294
"Must setup local AWS configuration with a region supported by SageMaker."
294295
)
295296

296-
self.sagemaker_client = sagemaker_client or self.boto_session.client("sagemaker")
297-
prepend_user_agent(self.sagemaker_client)
297+
# Make use of user_agent_extra field of the botocore_config object
298+
# to append SageMaker Python SDK specific user_agent suffix
299+
# to the current User-Agent header value from boto3
300+
# This config will also make sure that user_agent never fails to log the User-Agent string
301+
# even if boto User-Agent header format is updated in the future
302+
# Ref: https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html
303+
botocore_config = botocore.config.Config(user_agent_extra=get_user_agent_extra_suffix())
304+
305+
# Create sagemaker_client with the botocore_config object
306+
# This config is customized to append SageMaker Python SDK specific user_agent suffix
307+
self.sagemaker_client = sagemaker_client or self.boto_session.client(
308+
"sagemaker", config=botocore_config
309+
)
298310

299311
if sagemaker_runtime_client is not None:
300312
self.sagemaker_runtime_client = sagemaker_runtime_client
301313
else:
302-
config = botocore.config.Config(read_timeout=80)
314+
config = botocore.config.Config(
315+
read_timeout=80, user_agent_extra=get_user_agent_extra_suffix()
316+
)
303317
self.sagemaker_runtime_client = self.boto_session.client(
304318
"runtime.sagemaker", config=config
305319
)
306320

307-
prepend_user_agent(self.sagemaker_runtime_client)
308-
309321
if sagemaker_featurestore_runtime_client:
310322
self.sagemaker_featurestore_runtime_client = sagemaker_featurestore_runtime_client
311323
else:
@@ -316,8 +328,9 @@ def _initialize(
316328
if sagemaker_metrics_client:
317329
self.sagemaker_metrics_client = sagemaker_metrics_client
318330
else:
319-
self.sagemaker_metrics_client = self.boto_session.client("sagemaker-metrics")
320-
prepend_user_agent(self.sagemaker_metrics_client)
331+
self.sagemaker_metrics_client = self.boto_session.client(
332+
"sagemaker-metrics", config=botocore_config
333+
)
321334

322335
self.s3_client = self.boto_session.client("s3", region_name=self.boto_region_name)
323336
self.s3_resource = self.boto_session.resource("s3", region_name=self.boto_region_name)

src/sagemaker/user_agent.py

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
"""Placeholder docstring"""
1414
from __future__ import absolute_import
1515

16-
import platform
17-
import sys
1816
import json
1917
import os
2018

@@ -28,12 +26,6 @@
2826
STUDIO_METADATA_FILE = "/opt/ml/metadata/resource-metadata.json"
2927

3028
SDK_VERSION = importlib_metadata.version("sagemaker")
31-
OS_NAME = platform.system() or "UnresolvedOS"
32-
OS_VERSION = platform.release() or "UnresolvedOSVersion"
33-
OS_NAME_VERSION = "{}/{}".format(OS_NAME, OS_VERSION)
34-
PYTHON_VERSION = "Python/{}.{}.{}".format(
35-
sys.version_info.major, sys.version_info.minor, sys.version_info.micro
36-
)
3729

3830

3931
def process_notebook_metadata_file():
@@ -63,45 +55,24 @@ def process_studio_metadata_file():
6355
return None
6456

6557

66-
def determine_prefix(user_agent=""):
67-
"""Determines the prefix for the user agent string.
58+
def get_user_agent_extra_suffix():
59+
"""Get the user agent extra suffix string specific to SageMaker Python SDK
6860
69-
Args:
70-
user_agent (str): The user agent string to prepend the prefix to.
61+
Adhers to new boto recommended User-Agent 2.0 header format
7162
7263
Returns:
73-
str: The user agent string with the prefix prepended.
64+
str: The user agent extra suffix string to be appended
7465
"""
75-
prefix = "{}/{}".format(SDK_PREFIX, SDK_VERSION)
76-
77-
if PYTHON_VERSION not in user_agent:
78-
prefix = "{} {}".format(prefix, PYTHON_VERSION)
79-
80-
if OS_NAME_VERSION not in user_agent:
81-
prefix = "{} {}".format(prefix, OS_NAME_VERSION)
66+
suffix = "lib/{}#{}".format(SDK_PREFIX, SDK_VERSION)
8267

8368
# Get the notebook instance type and prepend it to the user agent string if exists
8469
notebook_instance_type = process_notebook_metadata_file()
8570
if notebook_instance_type:
86-
prefix = "{} {}/{}".format(prefix, NOTEBOOK_PREFIX, notebook_instance_type)
71+
suffix = "{} md/{}#{}".format(suffix, NOTEBOOK_PREFIX, notebook_instance_type)
8772

8873
# Get the studio app type and prepend it to the user agent string if exists
8974
studio_app_type = process_studio_metadata_file()
9075
if studio_app_type:
91-
prefix = "{} {}/{}".format(prefix, STUDIO_PREFIX, studio_app_type)
92-
93-
return prefix
94-
95-
96-
def prepend_user_agent(client):
97-
"""Prepends the user agent string with the SageMaker Python SDK version.
98-
99-
Args:
100-
client (botocore.client.BaseClient): The client to prepend the user agent string for.
101-
"""
102-
prefix = determine_prefix(client._client_config.user_agent)
76+
suffix = "{} md/{}#{}".format(suffix, STUDIO_PREFIX, studio_app_type)
10377

104-
if client._client_config.user_agent is None:
105-
client._client_config.user_agent = prefix
106-
else:
107-
client._client_config.user_agent = "{} {}".format(prefix, client._client_config.user_agent)
78+
return suffix

tests/unit/test_session.py

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@
4343
from sagemaker.utils import update_list_of_dicts_with_values_from_config
4444
from sagemaker.user_agent import (
4545
SDK_PREFIX,
46-
STUDIO_PREFIX,
47-
NOTEBOOK_PREFIX,
4846
)
4947
from sagemaker.compute_resource_requirements.resource_requirements import ResourceRequirements
5048
from tests.unit import (
@@ -87,15 +85,20 @@
8785
limits={},
8886
)
8987

88+
SDK_DEFAULT_SUFFIX = f"lib/{SDK_PREFIX}#2.218.0"
89+
NOTEBOOK_SUFFIX = f"{SDK_DEFAULT_SUFFIX} md/AWS-SageMaker-Notebook-Instance#instance_type"
90+
STUDIO_SUFFIX = f"{SDK_DEFAULT_SUFFIX} md/AWS-SageMaker-Studio#app_type"
9091

91-
@pytest.fixture()
92-
def boto_session():
93-
boto_mock = Mock(name="boto_session", region_name=REGION)
9492

93+
@pytest.fixture
94+
def boto_session(request):
95+
boto_user_agent = "Boto3/1.33.9 md/Botocore#1.33.9 ua/2.0 os/linux#linux-ver md/arch#x86_64 lang/python#3.10.6"
96+
user_agent_suffix = getattr(request, "param", "")
97+
boto_mock = Mock(name="boto_session", region_name=REGION)
9598
client_mock = Mock()
96-
client_mock._client_config.user_agent = (
97-
"Boto3/1.9.69 Python/3.6.5 Linux/4.14.77-70.82.amzn1.x86_64 Botocore/1.12.69 Resource"
98-
)
99+
user_agent = f"{boto_user_agent} {SDK_DEFAULT_SUFFIX} {user_agent_suffix}"
100+
with patch("sagemaker.user_agent.get_user_agent_extra_suffix", return_value=user_agent_suffix):
101+
client_mock._client_config.user_agent = user_agent
99102
boto_mock.client.return_value = client_mock
100103
return boto_mock
101104

@@ -887,65 +890,42 @@ def test_delete_model(boto_session):
887890
boto_session.client().delete_model.assert_called_with(ModelName=model_name)
888891

889892

893+
@pytest.mark.parametrize("boto_session", [""], indirect=True)
890894
def test_user_agent_injected(boto_session):
891-
assert SDK_PREFIX not in boto_session.client("sagemaker")._client_config.user_agent
892-
893895
sess = Session(boto_session)
894-
896+
expected_user_agent_suffix = "lib/AWS-SageMaker-Python-SDK#2.218.0"
895897
for client in [
896898
sess.sagemaker_client,
897899
sess.sagemaker_runtime_client,
898900
sess.sagemaker_metrics_client,
899901
]:
900-
assert SDK_PREFIX in client._client_config.user_agent
901-
assert NOTEBOOK_PREFIX not in client._client_config.user_agent
902-
assert STUDIO_PREFIX not in client._client_config.user_agent
902+
assert expected_user_agent_suffix in client._client_config.user_agent
903903

904904

905-
@patch("sagemaker.user_agent.process_notebook_metadata_file", return_value="ml.t3.medium")
906-
def test_user_agent_injected_with_nbi(
907-
mock_process_notebook_metadata_file,
908-
boto_session,
909-
):
910-
assert SDK_PREFIX not in boto_session.client("sagemaker")._client_config.user_agent
911-
912-
sess = Session(
913-
boto_session=boto_session,
905+
@pytest.mark.parametrize("boto_session", [f"{NOTEBOOK_SUFFIX}"], indirect=True)
906+
def test_user_agent_with_notebook_instance_type(boto_session):
907+
sess = Session(boto_session)
908+
expected_user_agent_suffix = (
909+
"lib/AWS-SageMaker-Python-SDK#2.218.0 md/AWS-SageMaker-Notebook-Instance#instance_type"
914910
)
915-
916911
for client in [
917912
sess.sagemaker_client,
918913
sess.sagemaker_runtime_client,
919914
sess.sagemaker_metrics_client,
920915
]:
921-
mock_process_notebook_metadata_file.assert_called()
922-
923-
assert SDK_PREFIX in client._client_config.user_agent
924-
assert NOTEBOOK_PREFIX in client._client_config.user_agent
925-
assert STUDIO_PREFIX not in client._client_config.user_agent
916+
assert expected_user_agent_suffix in client._client_config.user_agent
926917

927918

928-
@patch("sagemaker.user_agent.process_studio_metadata_file", return_value="dymmy-app-type")
929-
def test_user_agent_injected_with_studio_app_type(
930-
mock_process_studio_metadata_file,
931-
boto_session,
932-
):
933-
assert SDK_PREFIX not in boto_session.client("sagemaker")._client_config.user_agent
934-
935-
sess = Session(
936-
boto_session=boto_session,
937-
)
938-
919+
@pytest.mark.parametrize("boto_session", [f"{STUDIO_SUFFIX}"], indirect=True)
920+
def test_user_agent_with_studio_app_type(boto_session):
921+
sess = Session(boto_session)
922+
expected_user_agent = "lib/AWS-SageMaker-Python-SDK#2.218.0 md/AWS-SageMaker-Studio#app_type"
939923
for client in [
940924
sess.sagemaker_client,
941925
sess.sagemaker_runtime_client,
942926
sess.sagemaker_metrics_client,
943927
]:
944-
mock_process_studio_metadata_file.assert_called()
945-
946-
assert SDK_PREFIX in client._client_config.user_agent
947-
assert NOTEBOOK_PREFIX not in client._client_config.user_agent
948-
assert STUDIO_PREFIX in client._client_config.user_agent
928+
assert expected_user_agent in client._client_config.user_agent
949929

950930

951931
def test_training_input_all_defaults():

tests/unit/test_user_agent.py

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,17 @@
1313
from __future__ import absolute_import
1414

1515
import json
16-
from mock import MagicMock, patch, mock_open
16+
from mock import patch, mock_open
1717

1818

1919
from sagemaker.user_agent import (
2020
SDK_PREFIX,
2121
SDK_VERSION,
22-
PYTHON_VERSION,
23-
OS_NAME_VERSION,
2422
NOTEBOOK_PREFIX,
2523
STUDIO_PREFIX,
2624
process_notebook_metadata_file,
2725
process_studio_metadata_file,
28-
determine_prefix,
29-
prepend_user_agent,
26+
get_user_agent_extra_suffix,
3027
)
3128

3229

@@ -60,45 +57,18 @@ def test_process_studio_metadata_file_not_exists(tmp_path):
6057
assert process_studio_metadata_file() is None
6158

6259

63-
# Test determine_prefix function
64-
def test_determine_prefix_notebook_instance_type(monkeypatch):
65-
monkeypatch.setattr(
66-
"sagemaker.user_agent.process_notebook_metadata_file", lambda: "instance_type"
67-
)
68-
assert (
69-
determine_prefix()
70-
== f"{SDK_PREFIX}/{SDK_VERSION} {PYTHON_VERSION} {OS_NAME_VERSION} {NOTEBOOK_PREFIX}/instance_type"
71-
)
72-
73-
74-
def test_determine_prefix_studio_app_type(monkeypatch):
75-
monkeypatch.setattr(
76-
"sagemaker.user_agent.process_studio_metadata_file", lambda: "studio_app_type"
77-
)
78-
assert (
79-
determine_prefix()
80-
== f"{SDK_PREFIX}/{SDK_VERSION} {PYTHON_VERSION} {OS_NAME_VERSION} {STUDIO_PREFIX}/studio_app_type"
81-
)
82-
83-
84-
def test_determine_prefix_no_metadata(monkeypatch):
85-
monkeypatch.setattr("sagemaker.user_agent.process_notebook_metadata_file", lambda: None)
86-
monkeypatch.setattr("sagemaker.user_agent.process_studio_metadata_file", lambda: None)
87-
assert determine_prefix() == f"{SDK_PREFIX}/{SDK_VERSION} {PYTHON_VERSION} {OS_NAME_VERSION}"
88-
89-
90-
# Test prepend_user_agent function
91-
def test_prepend_user_agent_existing_user_agent(monkeypatch):
92-
client = MagicMock()
93-
client._client_config.user_agent = "existing_user_agent"
94-
monkeypatch.setattr("sagemaker.user_agent.determine_prefix", lambda _: "prefix")
95-
prepend_user_agent(client)
96-
assert client._client_config.user_agent == "prefix existing_user_agent"
97-
98-
99-
def test_prepend_user_agent_no_user_agent(monkeypatch):
100-
client = MagicMock()
101-
client._client_config.user_agent = None
102-
monkeypatch.setattr("sagemaker.user_agent.determine_prefix", lambda _: "prefix")
103-
prepend_user_agent(client)
104-
assert client._client_config.user_agent == "prefix"
60+
# Test get_user_agent_extra_suffix function
61+
def test_get_user_agent_extra_suffix():
62+
assert get_user_agent_extra_suffix() == f"lib/{SDK_PREFIX}#{SDK_VERSION}"
63+
64+
with patch("sagemaker.user_agent.process_notebook_metadata_file", return_value="instance_type"):
65+
assert (
66+
get_user_agent_extra_suffix()
67+
== f"lib/{SDK_PREFIX}#{SDK_VERSION} md/{NOTEBOOK_PREFIX}#instance_type"
68+
)
69+
70+
with patch("sagemaker.user_agent.process_studio_metadata_file", return_value="studio_type"):
71+
assert (
72+
get_user_agent_extra_suffix()
73+
== f"lib/{SDK_PREFIX}#{SDK_VERSION} md/{STUDIO_PREFIX}#studio_type"
74+
)

0 commit comments

Comments
 (0)