16
16
import pytest
17
17
import yaml
18
18
import logging
19
- from mock import Mock , MagicMock , patch
19
+ from mock import Mock , MagicMock , patch , call
20
20
21
21
from sagemaker .config .config import (
22
22
load_local_mode_config ,
23
23
load_sagemaker_config ,
24
24
logger ,
25
+ non_repeating_log_factory ,
25
26
_DEFAULT_ADMIN_CONFIG_FILE_PATH ,
26
27
_DEFAULT_USER_CONFIG_FILE_PATH ,
27
28
_DEFAULT_LOCAL_MODE_CONFIG_FILE_PATH ,
@@ -45,14 +46,14 @@ def expected_merged_config(get_data_dir):
45
46
46
47
47
48
def test_config_when_default_config_file_and_user_config_file_is_not_found ():
48
- assert load_sagemaker_config () == {}
49
+ assert load_sagemaker_config (repeat_log = True ) == {}
49
50
50
51
51
52
def test_config_when_overriden_default_config_file_is_not_found (get_data_dir ):
52
53
fake_config_file_path = os .path .join (get_data_dir , "config-not-found.yaml" )
53
54
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = fake_config_file_path
54
55
with pytest .raises (ValueError ):
55
- load_sagemaker_config ()
56
+ load_sagemaker_config (repeat_log = True )
56
57
del os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ]
57
58
58
59
@@ -63,14 +64,14 @@ def test_invalid_config_file_which_has_python_code(get_data_dir):
63
64
# PyYAML will throw exceptions for yaml.safe_load. SageMaker Config is using
64
65
# yaml.safe_load internally
65
66
with pytest .raises (ConstructorError ) as exception_info :
66
- load_sagemaker_config (additional_config_paths = [invalid_config_file_path ])
67
+ load_sagemaker_config (additional_config_paths = [invalid_config_file_path ], repeat_log = True )
67
68
assert "python/object/apply:eval" in str (exception_info .value )
68
69
69
70
70
71
def test_config_when_additional_config_file_path_is_not_found (get_data_dir ):
71
72
fake_config_file_path = os .path .join (get_data_dir , "config-not-found.yaml" )
72
73
with pytest .raises (ValueError ):
73
- load_sagemaker_config (additional_config_paths = [fake_config_file_path ])
74
+ load_sagemaker_config (additional_config_paths = [fake_config_file_path ], repeat_log = True )
74
75
75
76
76
77
def test_config_factory_when_override_user_config_file_is_not_found (get_data_dir ):
@@ -79,15 +80,15 @@ def test_config_factory_when_override_user_config_file_is_not_found(get_data_dir
79
80
)
80
81
os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ] = fake_additional_override_config_file_path
81
82
with pytest .raises (ValueError ):
82
- load_sagemaker_config ()
83
+ load_sagemaker_config (repeat_log = True )
83
84
del os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ]
84
85
85
86
86
87
def test_default_config_file_with_invalid_schema (get_data_dir ):
87
88
config_file_path = os .path .join (get_data_dir , "invalid_config_file.yaml" )
88
89
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = config_file_path
89
90
with pytest .raises (exceptions .ValidationError ):
90
- load_sagemaker_config ()
91
+ load_sagemaker_config (repeat_log = True )
91
92
del os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ]
92
93
93
94
@@ -98,7 +99,7 @@ def test_default_config_file_when_directory_is_provided_as_the_path(
98
99
expected_config = base_config_with_schema
99
100
expected_config ["SageMaker" ] = valid_config_with_all_the_scopes
100
101
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = get_data_dir
101
- assert expected_config == load_sagemaker_config ()
102
+ assert expected_config == load_sagemaker_config (repeat_log = True )
102
103
del os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ]
103
104
104
105
@@ -108,7 +109,9 @@ def test_additional_config_paths_when_directory_is_provided(
108
109
# This will try to load config.yaml file from that directory if present.
109
110
expected_config = base_config_with_schema
110
111
expected_config ["SageMaker" ] = valid_config_with_all_the_scopes
111
- assert expected_config == load_sagemaker_config (additional_config_paths = [get_data_dir ])
112
+ assert expected_config == load_sagemaker_config (
113
+ additional_config_paths = [get_data_dir ], repeat_log = True
114
+ )
112
115
113
116
114
117
def test_default_config_file_when_path_is_provided_as_environment_variable (
@@ -118,7 +121,7 @@ def test_default_config_file_when_path_is_provided_as_environment_variable(
118
121
# This will try to load config.yaml file from that directory if present.
119
122
expected_config = base_config_with_schema
120
123
expected_config ["SageMaker" ] = valid_config_with_all_the_scopes
121
- assert expected_config == load_sagemaker_config ()
124
+ assert expected_config == load_sagemaker_config (repeat_log = True )
122
125
del os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ]
123
126
124
127
@@ -131,7 +134,9 @@ def test_merge_behavior_when_additional_config_file_path_is_not_found(
131
134
)
132
135
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = valid_config_file_path
133
136
with pytest .raises (ValueError ):
134
- load_sagemaker_config (additional_config_paths = [fake_additional_override_config_file_path ])
137
+ load_sagemaker_config (
138
+ additional_config_paths = [fake_additional_override_config_file_path ], repeat_log = True
139
+ )
135
140
del os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ]
136
141
137
142
@@ -142,10 +147,10 @@ def test_merge_behavior(get_data_dir, expected_merged_config):
142
147
)
143
148
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = valid_config_file_path
144
149
assert expected_merged_config == load_sagemaker_config (
145
- additional_config_paths = [additional_override_config_file_path ]
150
+ additional_config_paths = [additional_override_config_file_path ], repeat_log = True
146
151
)
147
152
os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ] = additional_override_config_file_path
148
- assert expected_merged_config == load_sagemaker_config ()
153
+ assert expected_merged_config == load_sagemaker_config (repeat_log = True )
149
154
del os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ]
150
155
del os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ]
151
156
@@ -169,7 +174,7 @@ def test_s3_config_file(
169
174
expected_config = base_config_with_schema
170
175
expected_config ["SageMaker" ] = valid_config_with_all_the_scopes
171
176
assert expected_config == load_sagemaker_config (
172
- additional_config_paths = [config_file_s3_uri ], s3_resource = s3_resource_mock
177
+ additional_config_paths = [config_file_s3_uri ], s3_resource = s3_resource_mock , repeat_log = True
173
178
)
174
179
175
180
@@ -183,7 +188,9 @@ def test_config_factory_when_default_s3_config_file_is_not_found(s3_resource_moc
183
188
config_file_s3_uri = "s3://{}/{}" .format (config_file_bucket , config_file_s3_prefix )
184
189
with pytest .raises (ValueError ):
185
190
load_sagemaker_config (
186
- additional_config_paths = [config_file_s3_uri ], s3_resource = s3_resource_mock
191
+ additional_config_paths = [config_file_s3_uri ],
192
+ s3_resource = s3_resource_mock ,
193
+ repeat_log = True ,
187
194
)
188
195
189
196
@@ -213,7 +220,7 @@ def test_s3_config_file_when_uri_provided_corresponds_to_a_path(
213
220
expected_config = base_config_with_schema
214
221
expected_config ["SageMaker" ] = valid_config_with_all_the_scopes
215
222
assert expected_config == load_sagemaker_config (
216
- additional_config_paths = [config_file_s3_uri ], s3_resource = s3_resource_mock
223
+ additional_config_paths = [config_file_s3_uri ], s3_resource = s3_resource_mock , repeat_log = True
217
224
)
218
225
219
226
@@ -242,6 +249,7 @@ def test_merge_of_s3_default_config_file_and_regular_config_file(
242
249
assert expected_merged_config == load_sagemaker_config (
243
250
additional_config_paths = [additional_override_config_file_path ],
244
251
s3_resource = s3_resource_mock ,
252
+ repeat_log = True ,
245
253
)
246
254
del os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ]
247
255
@@ -254,7 +262,7 @@ def test_logging_when_overridden_admin_is_found_and_overridden_user_config_is_fo
254
262
255
263
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = get_data_dir
256
264
os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ] = get_data_dir
257
- load_sagemaker_config ()
265
+ load_sagemaker_config (repeat_log = True )
258
266
assert "Fetched defaults config from location: {}" .format (get_data_dir ) in caplog .text
259
267
assert (
260
268
"Not applying SDK defaults from location: {}" .format (_DEFAULT_ADMIN_CONFIG_FILE_PATH )
@@ -275,7 +283,7 @@ def test_logging_when_overridden_admin_is_found_and_default_user_config_not_foun
275
283
logger .propagate = True
276
284
caplog .set_level (logging .DEBUG , logger = logger .name )
277
285
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = get_data_dir
278
- load_sagemaker_config ()
286
+ load_sagemaker_config (repeat_log = True )
279
287
assert "Fetched defaults config from location: {}" .format (get_data_dir ) in caplog .text
280
288
assert (
281
289
"Not applying SDK defaults from location: {}" .format (_DEFAULT_USER_CONFIG_FILE_PATH )
@@ -297,7 +305,7 @@ def test_logging_when_default_admin_not_found_and_overriden_user_config_is_found
297
305
logger .propagate = True
298
306
caplog .set_level (logging .DEBUG , logger = logger .name )
299
307
os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ] = get_data_dir
300
- load_sagemaker_config ()
308
+ load_sagemaker_config (repeat_log = True )
301
309
assert "Fetched defaults config from location: {}" .format (get_data_dir ) in caplog .text
302
310
assert (
303
311
"Not applying SDK defaults from location: {}" .format (_DEFAULT_ADMIN_CONFIG_FILE_PATH )
@@ -318,7 +326,7 @@ def test_logging_when_default_admin_not_found_and_default_user_config_not_found(
318
326
# for admin and user config since both are missing from default location
319
327
logger .propagate = True
320
328
caplog .set_level (logging .DEBUG , logger = logger .name )
321
- load_sagemaker_config ()
329
+ load_sagemaker_config (repeat_log = True )
322
330
assert (
323
331
"Not applying SDK defaults from location: {}" .format (_DEFAULT_ADMIN_CONFIG_FILE_PATH )
324
332
in caplog .text
@@ -342,6 +350,26 @@ def test_logging_when_default_admin_not_found_and_default_user_config_not_found(
342
350
logger .propagate = False
343
351
344
352
353
+ @patch ("sagemaker.config.config.log_info_function" )
354
+ def test_load_config_without_repeating_log (log_info ):
355
+
356
+ load_sagemaker_config (repeat_log = False )
357
+ assert log_info .call_count == 2
358
+ log_info .assert_has_calls (
359
+ [
360
+ call (
361
+ "Not applying SDK defaults from location: %s" ,
362
+ _DEFAULT_ADMIN_CONFIG_FILE_PATH ,
363
+ ),
364
+ call (
365
+ "Not applying SDK defaults from location: %s" ,
366
+ _DEFAULT_USER_CONFIG_FILE_PATH ,
367
+ ),
368
+ ],
369
+ any_order = True ,
370
+ )
371
+
372
+
345
373
def test_logging_when_default_admin_not_found_and_overriden_user_config_not_found (
346
374
get_data_dir , caplog
347
375
):
@@ -351,7 +379,7 @@ def test_logging_when_default_admin_not_found_and_overriden_user_config_not_foun
351
379
fake_config_file_path = os .path .join (get_data_dir , "config-not-found.yaml" )
352
380
os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ] = fake_config_file_path
353
381
with pytest .raises (ValueError ):
354
- load_sagemaker_config ()
382
+ load_sagemaker_config (repeat_log = True )
355
383
assert (
356
384
"Not applying SDK defaults from location: {}" .format (_DEFAULT_ADMIN_CONFIG_FILE_PATH )
357
385
in caplog .text
@@ -374,7 +402,7 @@ def test_logging_when_overriden_admin_not_found_and_overridden_user_config_not_f
374
402
os .environ ["SAGEMAKER_USER_CONFIG_OVERRIDE" ] = fake_config_file_path
375
403
os .environ ["SAGEMAKER_ADMIN_CONFIG_OVERRIDE" ] = fake_config_file_path
376
404
with pytest .raises (ValueError ):
377
- load_sagemaker_config ()
405
+ load_sagemaker_config (repeat_log = True )
378
406
assert (
379
407
"Not applying SDK defaults from location: {}" .format (_DEFAULT_ADMIN_CONFIG_FILE_PATH )
380
408
not in caplog .text
@@ -394,7 +422,7 @@ def test_logging_with_additional_configs_and_none_are_found(caplog):
394
422
# Should throw exception when config in additional_config_path is missing
395
423
logger .propagate = True
396
424
with pytest .raises (ValueError ):
397
- load_sagemaker_config (additional_config_paths = ["fake-path" ])
425
+ load_sagemaker_config (additional_config_paths = ["fake-path" ], repeat_log = True )
398
426
assert (
399
427
"Not applying SDK defaults from location: {}" .format (_DEFAULT_ADMIN_CONFIG_FILE_PATH )
400
428
in caplog .text
@@ -414,3 +442,37 @@ def test_load_local_mode_config(mock_load_config):
414
442
415
443
def test_load_local_mode_config_when_config_file_is_not_found ():
416
444
assert load_local_mode_config () is None
445
+
446
+
447
+ @pytest .mark .parametrize (
448
+ "method_name" ,
449
+ ["info" , "warning" , "debug" ],
450
+ )
451
+ def test_non_repeating_log_factory (method_name ):
452
+ tmp_logger = logging .getLogger ("test-logger" )
453
+ mock = MagicMock ()
454
+ setattr (tmp_logger , method_name , mock )
455
+
456
+ log_function = non_repeating_log_factory (tmp_logger , method_name )
457
+ log_function ("foo" )
458
+ log_function ("foo" )
459
+
460
+ mock .assert_called_once ()
461
+
462
+
463
+ @pytest .mark .parametrize (
464
+ "method_name" ,
465
+ ["info" , "warning" , "debug" ],
466
+ )
467
+ def test_non_repeating_log_factory_cache_size (method_name ):
468
+ tmp_logger = logging .getLogger ("test-logger" )
469
+ mock = MagicMock ()
470
+ setattr (tmp_logger , method_name , mock )
471
+
472
+ log_function = non_repeating_log_factory (tmp_logger , method_name , cache_size = 2 )
473
+ log_function ("foo" )
474
+ log_function ("bar" )
475
+ log_function ("foo2" )
476
+ log_function ("foo" )
477
+
478
+ assert mock .call_count == 4
0 commit comments