Skip to content

Commit f5abeac

Browse files
committed
fix: Pipeline TypeError: can only concatenate list (not "NoneType") to list Using Sou (5518)
1 parent ee420cc commit f5abeac

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

sagemaker-core/tests/unit/workflow/test_utilities.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
get_processing_dependencies,
2525
get_processing_code_hash,
2626
get_training_code_hash,
27+
get_code_hash,
2728
validate_step_args_input,
2829
override_pipeline_parameter_var,
2930
trim_request_dict,
@@ -285,6 +286,20 @@ def test_get_training_code_hash_with_source_dir(self):
285286
assert len(result_with_deps) == 64
286287
assert result_no_deps != result_with_deps
287288

289+
def test_get_training_code_hash_with_source_dir_and_none_dependencies(self):
290+
"""Test get_training_code_hash with source_dir and None dependencies does not raise TypeError"""
291+
with tempfile.TemporaryDirectory() as temp_dir:
292+
entry_file = Path(temp_dir, "train.py")
293+
entry_file.write_text("print('training')")
294+
295+
# This should NOT raise TypeError: can only concatenate list (not "NoneType") to list
296+
result = get_training_code_hash(
297+
entry_point=str(entry_file), source_dir=temp_dir, dependencies=None
298+
)
299+
300+
assert result is not None
301+
assert len(result) == 64
302+
288303
def test_get_training_code_hash_entry_point_only(self):
289304
"""Test get_training_code_hash with entry_point only"""
290305
with tempfile.TemporaryDirectory() as temp_dir:
@@ -308,6 +323,20 @@ def test_get_training_code_hash_entry_point_only(self):
308323
assert len(result_with_deps) == 64
309324
assert result_no_deps != result_with_deps
310325

326+
def test_get_training_code_hash_entry_point_only_and_none_dependencies(self):
327+
"""Test get_training_code_hash with entry_point only and None dependencies does not raise TypeError"""
328+
with tempfile.TemporaryDirectory() as temp_dir:
329+
entry_file = Path(temp_dir, "train.py")
330+
entry_file.write_text("print('training')")
331+
332+
# This should NOT raise TypeError: can only concatenate list (not "NoneType") to list
333+
result = get_training_code_hash(
334+
entry_point=str(entry_file), source_dir=None, dependencies=None
335+
)
336+
337+
assert result is not None
338+
assert len(result) == 64
339+
311340
def test_get_training_code_hash_s3_uri(self):
312341
"""Test get_training_code_hash with S3 URI returns None"""
313342
result = get_training_code_hash(
@@ -325,6 +354,77 @@ def test_get_training_code_hash_pipeline_variable(self):
325354

326355
assert result is None
327356

357+
@pytest.mark.skip(reason="Requires sagemaker-mlops module which is not installed in sagemaker-core tests")
358+
def test_get_code_hash_with_training_step_and_no_requirements(self):
359+
"""Test get_code_hash with TrainingStep where SourceCode has requirements=None"""
360+
from sagemaker.mlops.workflow.steps import TrainingStep
361+
362+
with tempfile.TemporaryDirectory() as temp_dir:
363+
entry_file = Path(temp_dir, "train.py")
364+
entry_file.write_text("print('training')")
365+
366+
mock_source_code = Mock()
367+
mock_source_code.source_dir = temp_dir
368+
mock_source_code.requirements = None # This is the key: requirements is None
369+
mock_source_code.entry_script = str(entry_file)
370+
371+
mock_model_trainer = Mock()
372+
mock_model_trainer.source_code = mock_source_code
373+
374+
mock_step_args = Mock()
375+
mock_step_args.func_args = [mock_model_trainer]
376+
377+
mock_step = Mock(spec=TrainingStep)
378+
mock_step.step_args = mock_step_args
379+
380+
# This should NOT raise TypeError
381+
result = get_code_hash(mock_step)
382+
383+
assert result is not None
384+
assert len(result) == 64
385+
386+
@pytest.mark.skip(reason="Requires sagemaker-mlops module which is not installed in sagemaker-core tests")
387+
def test_get_code_hash_with_training_step_and_requirements(self):
388+
"""Test get_code_hash with TrainingStep where SourceCode has valid requirements"""
389+
from sagemaker.mlops.workflow.steps import TrainingStep
390+
391+
with tempfile.TemporaryDirectory() as temp_dir:
392+
entry_file = Path(temp_dir, "train.py")
393+
entry_file.write_text("print('training')")
394+
requirements_file = Path(temp_dir, "requirements.txt")
395+
requirements_file.write_text("numpy==1.21.0")
396+
397+
mock_source_code_no_req = Mock()
398+
mock_source_code_no_req.source_dir = temp_dir
399+
mock_source_code_no_req.requirements = None
400+
mock_source_code_no_req.entry_script = str(entry_file)
401+
402+
mock_source_code_with_req = Mock()
403+
mock_source_code_with_req.source_dir = temp_dir
404+
mock_source_code_with_req.requirements = str(requirements_file)
405+
mock_source_code_with_req.entry_script = str(entry_file)
406+
407+
mock_model_trainer_no_req = Mock()
408+
mock_model_trainer_no_req.source_code = mock_source_code_no_req
409+
410+
mock_model_trainer_with_req = Mock()
411+
mock_model_trainer_with_req.source_code = mock_source_code_with_req
412+
413+
mock_step_no_req = Mock(spec=TrainingStep)
414+
mock_step_no_req.step_args = Mock()
415+
mock_step_no_req.step_args.func_args = [mock_model_trainer_no_req]
416+
417+
mock_step_with_req = Mock(spec=TrainingStep)
418+
mock_step_with_req.step_args = Mock()
419+
mock_step_with_req.step_args.func_args = [mock_model_trainer_with_req]
420+
421+
result_no_req = get_code_hash(mock_step_no_req)
422+
result_with_req = get_code_hash(mock_step_with_req)
423+
424+
assert result_no_req is not None
425+
assert result_with_req is not None
426+
assert result_no_req != result_with_req
427+
328428
def test_validate_step_args_input_valid(self):
329429
"""Test validate_step_args_input with valid input"""
330430
step_args = _StepArguments(

0 commit comments

Comments
 (0)