sagemaker-core 1.0.9__tar.gz → 1.0.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sagemaker-core might be problematic. Click here for more details.
- {sagemaker_core-1.0.9/src/sagemaker_core.egg-info → sagemaker_core-1.0.11}/PKG-INFO +1 -1
- sagemaker_core-1.0.11/VERSION +1 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/code_injection/shape_dag.py +15 -0
- sagemaker_core-1.0.11/src/sagemaker_core/main/logs.py +167 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/resources.py +200 -56
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/shapes.py +18 -14
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/utils.py +9 -3
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/constants.py +2 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/resources_codegen.py +47 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/templates.py +42 -6
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11/src/sagemaker_core.egg-info}/PKG-INFO +1 -1
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core.egg-info/SOURCES.txt +1 -0
- sagemaker_core-1.0.9/VERSION +0 -1
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/LICENSE +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/MANIFEST.in +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/README.rst +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/pyproject.toml +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/setup.cfg +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/__init__.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/_version.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/helper/__init__.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/helper/session_helper.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/__init__.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/code_injection/__init__.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/code_injection/base.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/code_injection/codec.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/code_injection/constants.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/config_schema.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/exceptions.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/intelligent_defaults_helper.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/user_agent.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/resources/__init__.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/shapes/__init__.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/__init__.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/codegen.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/data_extractor.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/method.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/resources_extractor.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/shapes_codegen.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/tools/shapes_extractor.py +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core.egg-info/dependency_links.txt +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core.egg-info/requires.txt +0 -0
- {sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.11
|
{sagemaker_core-1.0.9 → sagemaker_core-1.0.11}/src/sagemaker_core/main/code_injection/shape_dag.py
RENAMED
|
@@ -1380,6 +1380,11 @@ SHAPE_DAG = {
|
|
|
1380
1380
|
"shape": "AppLifecycleManagement",
|
|
1381
1381
|
"type": "structure",
|
|
1382
1382
|
},
|
|
1383
|
+
{
|
|
1384
|
+
"name": "BuiltInLifecycleConfigArn",
|
|
1385
|
+
"shape": "StudioLifecycleConfigArn",
|
|
1386
|
+
"type": "string",
|
|
1387
|
+
},
|
|
1383
1388
|
],
|
|
1384
1389
|
"type": "structure",
|
|
1385
1390
|
},
|
|
@@ -3793,6 +3798,11 @@ SHAPE_DAG = {
|
|
|
3793
3798
|
{"name": "CreationTime", "shape": "Timestamp", "type": "timestamp"},
|
|
3794
3799
|
{"name": "FailureReason", "shape": "FailureReason", "type": "string"},
|
|
3795
3800
|
{"name": "ResourceSpec", "shape": "ResourceSpec", "type": "structure"},
|
|
3801
|
+
{
|
|
3802
|
+
"name": "BuiltInLifecycleConfigArn",
|
|
3803
|
+
"shape": "StudioLifecycleConfigArn",
|
|
3804
|
+
"type": "string",
|
|
3805
|
+
},
|
|
3796
3806
|
],
|
|
3797
3807
|
"type": "structure",
|
|
3798
3808
|
},
|
|
@@ -7699,6 +7709,11 @@ SHAPE_DAG = {
|
|
|
7699
7709
|
"type": "structure",
|
|
7700
7710
|
},
|
|
7701
7711
|
{"name": "EmrSettings", "shape": "EmrSettings", "type": "structure"},
|
|
7712
|
+
{
|
|
7713
|
+
"name": "BuiltInLifecycleConfigArn",
|
|
7714
|
+
"shape": "StudioLifecycleConfigArn",
|
|
7715
|
+
"type": "string",
|
|
7716
|
+
},
|
|
7702
7717
|
],
|
|
7703
7718
|
"type": "structure",
|
|
7704
7719
|
},
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
import botocore
|
|
3
|
+
|
|
4
|
+
from boto3.session import Session
|
|
5
|
+
import botocore.client
|
|
6
|
+
from botocore.config import Config
|
|
7
|
+
from typing import Generator, Tuple, List
|
|
8
|
+
from sagemaker_core.main.utils import SingletonMeta
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CloudWatchLogsClient(metaclass=SingletonMeta):
|
|
12
|
+
"""
|
|
13
|
+
A singleton class for creating a CloudWatchLogs client.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
client: botocore.client = None
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
if not self.client:
|
|
20
|
+
session = Session()
|
|
21
|
+
self.client = session.client(
|
|
22
|
+
"logs",
|
|
23
|
+
session.region_name,
|
|
24
|
+
config=Config(retries={"max_attempts": 10, "mode": "standard"}),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LogStreamHandler:
|
|
29
|
+
log_group_name: str = None
|
|
30
|
+
log_stream_name: str = None
|
|
31
|
+
stream_id: int = None
|
|
32
|
+
next_token: str = None
|
|
33
|
+
cw_client = None
|
|
34
|
+
|
|
35
|
+
def __init__(self, log_group_name: str, log_stream_name: str, stream_id: int):
|
|
36
|
+
self.log_group_name = log_group_name
|
|
37
|
+
self.log_stream_name = log_stream_name
|
|
38
|
+
self.cw_client = CloudWatchLogsClient().client
|
|
39
|
+
self.stream_id = stream_id
|
|
40
|
+
|
|
41
|
+
def get_latest_log_events(self) -> Generator[Tuple[str, dict], None, None]:
|
|
42
|
+
"""
|
|
43
|
+
This method gets all the latest log events for this stream that exist at this moment in time.
|
|
44
|
+
|
|
45
|
+
cw_client.get_log_events() always returns a nextForwardToken even if the current batch of events is empty.
|
|
46
|
+
You can keep calling cw_client.get_log_events() with the same token until a new batch of log events exist.
|
|
47
|
+
|
|
48
|
+
API Reference: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/logs/client/get_log_events.html
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Generator[tuple[str, dict], None, None]: Generator that yields a tuple that consists for two values
|
|
52
|
+
str: stream_name,
|
|
53
|
+
dict: event dict in format
|
|
54
|
+
{
|
|
55
|
+
"ingestionTime": number,
|
|
56
|
+
"message": "string",
|
|
57
|
+
"timestamp": number
|
|
58
|
+
}
|
|
59
|
+
"""
|
|
60
|
+
while True:
|
|
61
|
+
if not self.next_token:
|
|
62
|
+
token_args = {}
|
|
63
|
+
else:
|
|
64
|
+
token_args = {"nextToken": self.next_token}
|
|
65
|
+
|
|
66
|
+
response = self.cw_client.get_log_events(
|
|
67
|
+
logGroupName=self.log_group_name,
|
|
68
|
+
logStreamName=self.log_stream_name,
|
|
69
|
+
startFromHead=True,
|
|
70
|
+
**token_args,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self.next_token = response["nextForwardToken"]
|
|
74
|
+
if not response["events"]:
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
for event in response["events"]:
|
|
78
|
+
yield self.log_stream_name, event
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class MultiLogStreamHandler:
|
|
82
|
+
log_group_name: str = None
|
|
83
|
+
log_stream_name_prefix: str = None
|
|
84
|
+
expected_stream_count: int = None
|
|
85
|
+
streams: List[LogStreamHandler] = []
|
|
86
|
+
cw_client = None
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self, log_group_name: str, log_stream_name_prefix: str, expected_stream_count: int
|
|
90
|
+
):
|
|
91
|
+
self.log_group_name = log_group_name
|
|
92
|
+
self.log_stream_name_prefix = log_stream_name_prefix
|
|
93
|
+
self.expected_stream_count = expected_stream_count
|
|
94
|
+
self.cw_client = CloudWatchLogsClient().client
|
|
95
|
+
|
|
96
|
+
def get_latest_log_events(self) -> Generator[Tuple[str, dict], None, None]:
|
|
97
|
+
"""
|
|
98
|
+
This method gets all the latest log events from each stream that exist at this moment.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Generator[tuple[str, dict], None, None]: Generator that yields a tuple that consists for two values
|
|
102
|
+
str: stream_name,
|
|
103
|
+
dict: event dict in format -
|
|
104
|
+
{
|
|
105
|
+
"ingestionTime": number,
|
|
106
|
+
"message": "string",
|
|
107
|
+
"timestamp": number
|
|
108
|
+
}
|
|
109
|
+
"""
|
|
110
|
+
if not self.ready():
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
for stream in self.streams:
|
|
114
|
+
yield from stream.get_latest_log_events()
|
|
115
|
+
|
|
116
|
+
def ready(self) -> bool:
|
|
117
|
+
"""
|
|
118
|
+
Checks whether or not MultiLogStreamHandler is ready to serve new log events at this moment.
|
|
119
|
+
|
|
120
|
+
If self.streams is already set, return True.
|
|
121
|
+
Otherwise, check if the current number of log streams in the log group match the exptected stream count.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
bool: Whether or not MultiLogStreamHandler is ready to serve new log events.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
if len(self.streams) >= self.expected_stream_count:
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
response = self.cw_client.describe_log_streams(
|
|
132
|
+
logGroupName=self.log_group_name,
|
|
133
|
+
logStreamNamePrefix=self.log_stream_name_prefix + "/",
|
|
134
|
+
orderBy="LogStreamName",
|
|
135
|
+
)
|
|
136
|
+
stream_names = [stream["logStreamName"] for stream in response["logStreams"]]
|
|
137
|
+
|
|
138
|
+
next_token = response.get("nextToken")
|
|
139
|
+
while next_token:
|
|
140
|
+
response = self.cw_client.describe_log_streams(
|
|
141
|
+
logGroupName=self.log_group_name,
|
|
142
|
+
logStreamNamePrefix=self.log_stream_name_prefix + "/",
|
|
143
|
+
orderBy="LogStreamName",
|
|
144
|
+
nextToken=next_token,
|
|
145
|
+
)
|
|
146
|
+
stream_names.extend([stream["logStreamName"] for stream in response["logStreams"]])
|
|
147
|
+
next_token = response.get("nextToken", None)
|
|
148
|
+
|
|
149
|
+
if len(stream_names) >= self.expected_stream_count:
|
|
150
|
+
self.streams = [
|
|
151
|
+
LogStreamHandler(self.log_group_name, log_stream_name, index)
|
|
152
|
+
for index, log_stream_name in enumerate(stream_names)
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
return True
|
|
156
|
+
else:
|
|
157
|
+
# Log streams are created whenever a container starts writing to stdout/err,
|
|
158
|
+
# so if the stream count is less than the expected number, return False
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
except botocore.exceptions.ClientError as e:
|
|
162
|
+
# On the very first training job run on an account, there's no log group until
|
|
163
|
+
# the container starts logging, so ignore any errors thrown about that
|
|
164
|
+
if e.response["Error"]["Code"] == "ResourceNotFoundException":
|
|
165
|
+
return False
|
|
166
|
+
else:
|
|
167
|
+
raise
|