Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion sagemaker-train/src/sagemaker/train/model_trainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,9 @@ def _prepare_train_script(
execute_driver=execute_driver,
)

with open(os.path.join(tmp_dir.name, TRAIN_SCRIPT), "w") as f:
# The container runs Linux, so the script must be LF-only regardless of the
# host that generated it.
with open(os.path.join(tmp_dir.name, TRAIN_SCRIPT), "w", newline="\n") as f:
f.write(train_script)

@classmethod
Expand Down
22 changes: 22 additions & 0 deletions sagemaker-train/tests/unit/train/test_model_trainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2019,3 +2019,25 @@ def test_networking_intelligent_defaults_fills_subnets_on_existing(model_trainer
assert model_trainer.networking.subnets == NETWORKING_DEFAULT_SUBNETS
# pre-existing security_group_ids are preserved.
assert model_trainer.networking.security_group_ids == ["sg-preexisting"]


def test_prepare_train_script_writes_lf_line_endings(model_trainer):
"""sm_train.sh must use LF endings even when written on a CRLF-default host (Windows).

The generated script is always executed inside a Linux training container, so a
host that maps text-mode "\n" to "\r\n" (Windows) must not leak CRLF into it -
bash rejects a script whose first line is "\r" (see aws/sagemaker-python-sdk#5904).
"""
with tempfile.TemporaryDirectory() as tmp_dir_name:

class _FakeTmpDir:
name = tmp_dir_name

model_trainer._prepare_train_script(_FakeTmpDir(), DEFAULT_SOURCE_CODE)

script_path = os.path.join(tmp_dir_name, TRAIN_SCRIPT)
with open(script_path, "rb") as f:
raw = f.read()

assert b"\r\n" not in raw
assert raw.startswith(b"\n#!/bin/bash\n") or raw.startswith(b"#!/bin/bash\n")
Loading