Skip to content

fix: Python 3.14 tuner tags (#6554) and tuner source staging bucket (#6555) - #6578

Merged
jam-jee merged 3 commits into
aws:masterfrom
jam-jee:fix/tuner-source-upload-bucket-6555
Nov 12, 2026
Merged

jam-jee merged 3 commits into
aws:masterfrom
jam-jee:fix/tuner-source-upload-bucket-6555

Conversation

@jam-jee

@jam-jee jam-jee commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

Two fixes reported on the same customer case. One commit for each issue.

#6554: HyperParameterTuningJob.create rejects tuner tags on Python 3.14

Root cause. resources.py runs from sagemaker.core.shapes import * and later defines the resource class Tag (the ListTags resource). Annotations written above that class, such as HyperParameterTuningJob.create(tags: Optional[List[Tag]]), mean the shape only because Python 3.13 and earlier evaluate annotations when a function is defined. Python 3.14 evaluates them lazily (PEP 649), so the same text resolves to resources.Tag. Pydantic then rejects the shapes.Tag objects that HyperparameterTuner sends.

Annotations written below the class already resolved to resources.Tag on every version. That affects TrainingJob.create, TransformJob.create, Trial.create, TrainingPlan.create, TrainingSession.create, the other T*/U*/W* create() methods and Tag.add_tags. It is why ModelTrainer has to pass dicts.

Fix.

  • Resource codegen writes shape names listed in the new RESOURCE_SHADOWED_SHAPE_NAMES (only Tag) as shapes.<Name>, and adds from sagemaker.core import shapes to the generated imports (tools/constants.py, tools/shapes_extractor.py, tools/resources_codegen.py).
  • resources.py is regenerated with the codegen. Before the change, regenerating master reproduced resources.py byte for byte, so this diff comes only from this change: 75 tags annotations plus the import.
  • Every generated tags parameter now takes a shapes.Tag or a dict on every Python version.

Behavior change. The methods listed above, defined after class Tag, used to accept resources.Tag instances and reject shapes.Tag. Now they do the opposite. Nothing in the SDK passes resources.Tag to them; it is only used for Tag.get_all.

Not changed. Other names shared by a shape and a resource (Device, Workteam, Workforce, SubscribedWorkteam) are only referenced after their resource class, so their meaning does not depend on the Python version. They are left alone.

Tests.

  • tests/unit/generated/test_resources_shadowed_shapes.py:
    • An AST guard that fails if a bare annotation names a shape that a later resource class shadows.
    • A test over all 65 tagged create/add_tags methods: get_type_hints resolves tags to a type that validates shapes.Tag and dicts into shapes.Tag.
    • On master this file fails the guard plus the 9 methods defined after class Tag.
  • tests/unit/tools/test_shapes_extractor.py: covers qualified and default output for structure, list and map members.

#6555: tuner stages local source outside the trainer's staging bucket

#6559 fixed the KMS key for the tuner's sourcedir.tar.gz upload. The upload still went to session.default_bucket() with no default_bucket_prefix.

ModelTrainer.create_input_data_channel stages sm_drivers for the same tuning job. It resolves the bucket through _resolve_staging_bucket(), which falls back to the output-path bucket when the training role cannot write the default bucket, and it adds default_bucket_prefix to the key. The source archive now follows the same rules, so it lands next to sm_drivers and stays reachable by the role.

Tests. Two unit tests in test_tuner_driver_channels.py: one for the staging-bucket fallback (bucket and prefix) and one for default_bucket_prefix.

Verification

  • Python 3.12:
    • Full unit suites pass: sagemaker-core 4106 passed, sagemaker-train 3509 passed.
    • black, flake8 and pydocstyle are clean on the changed files.
  • Python 3.14 (python:3.14-slim container):
  • Not run: sagemaker-serve unit tests and live AWS calls.

Closes #6554
Closes #6555

HyperparameterTuner uploaded the local sourcedir.tar.gz straight to
session.default_bucket() under a key with no default_bucket_prefix.
ModelTrainer.create_input_data_channel, which stages the sm_drivers
channel for the same tuning job, resolves the bucket through
_resolve_staging_bucket() (falling back to the output-path bucket when
the training role cannot write the default bucket) and prefixes keys
with default_bucket_prefix. The source archive now follows the same
rules, so it lands next to sm_drivers and stays reachable by the role.

PR aws#6559 fixed the KMS half of issue aws#6555; this is the bucket half.

Closes aws#6555
X-AI-Prompt: Fix the open SDK issues from TT V2409806538 (aws#6555 bucket gap, aws#6554)
X-AI-Tool: kiro-cli
resources.py star-imports the shapes and later defines the resource
class Tag (ListTags). Annotations written above that class, such as
HyperParameterTuningJob.create(tags: Optional[List[Tag]]), meant the
shape only because Python <= 3.13 evaluates annotations eagerly. Python
3.14 evaluates them lazily (PEP 649), so they resolve to resources.Tag
and pydantic rejects the shapes.Tag objects HyperparameterTuner sends.
Annotations written below the class already resolved to resources.Tag
on every version (TrainingJob.create and eight others), which is why
ModelTrainer had to pass dicts.

Resource codegen now emits shapes listed in
RESOURCE_SHADOWED_SHAPE_NAMES (only Tag) as shapes.<Name> and imports
sagemaker.core.shapes; resources.py is regenerated, and the diff is
only the 75 tags annotations plus that import. Every generated tags
parameter now takes shapes.Tag or a dict on all Python versions. A
guard test fails if a bare annotation names a shape that a later
resource class shadows.

Closes aws#6554
X-AI-Prompt: Fix the open SDK issues from TT V2409806538 (aws#6555 bucket gap, aws#6554)
X-AI-Tool: kiro-cli
@jam-jee jam-jee closed this Nov 11, 2026
@jam-jee jam-jee reopened this Nov 11, 2026
@jam-jee
jam-jee deployed to auto-approve November 11, 2026 23:47 — with GitHub Actions Active
@jam-jee
jam-jee deployed to auto-approve November 11, 2026 23:47 — with GitHub Actions Active
@jam-jee
jam-jee deployed to auto-approve November 11, 2026 23:48 — with GitHub Actions Active

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI code review

The PR makes two independent fixes. (1) For #6554, it teaches the resource codegen to emit shape names that a same-named resource class shadows (only Tag) as shapes.Tag, adds a qualified from sagemaker.core import shapes import, regenerates resources.py, and adds a solid AST guard plus get_type_hints parametrized tests to lock the behavior across Python versions. (2) For #6555, it routes the tuner's local-source sourcedir.tar.gz upload through ModelTrainer._resolve_staging_bucket() and default_bucket_prefix, matching create_input_data_channel, with two new unit tests. The prefix-composition order (staging_prefix/default_bucket_prefix/key_prefix) correctly mirrors create_input_data_channel, and the codegen change is correctly scoped to resource codegen only (shapes codegen keeps bare names). The change looks correct and well tested; only a minor redundant-IAM-call observation.


sagemaker-train/src/sagemaker/train/tuner.py
588
low

`_build_driver_and_code_channels` now calls `model_trainer._resolve_staging_bucket()` here in addition to the call already made inside `create_input_data_channel` for the `sm_drivers` channel (model_trainer.py:1254). `_resolve_staging_bucket` performs an `iam:SimulatePrincipalPolicy` API call each time, so staging a local source dir for a tuning job incurs two IAM calls that resolve to the same bucket/prefix. Consider resolving the bucket once and reusing the result for both the channel and the source upload. false

Reviewed commit 1a08ce6. Automated review; verify before acting.

black 26.3.1 (pinned in requirements/tox/black_requirements.txt)
requires an empty line after a module docstring; the
codestyle-doc-tests (sagemaker-core) check failed on it.

X-AI-Prompt: Fix the failing sagemaker-core doc test on PR 6578
X-AI-Tool: kiro-cli
@jam-jee
jam-jee deployed to auto-approve November 12, 2026 18:00 — with GitHub Actions Active
@jam-jee
jam-jee deployed to auto-approve November 12, 2026 18:01 — with GitHub Actions Active
@jam-jee
jam-jee deployed to auto-approve November 12, 2026 18:01 — with GitHub Actions Active

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI code review

This PR fixes two issues. (1) For #6554, resource codegen now emits shapes.Tag (via a new RESOURCE_SHADOWED_SHAPE_NAMES set and a qualified_shape_names option on ShapesExtractor) and adds from sagemaker.core import shapes, so the regenerated resources.py annotates every tags parameter unambiguously regardless of Python 3.14 deferred annotation evaluation. (2) For #6555, the tuner's sourcedir.tar.gz upload now resolves the bucket/prefix through ModelTrainer._resolve_staging_bucket() and applies default_bucket_prefix, matching create_input_data_channel.

I verified the changes against the base repo: the shapes codegen path keeps the default empty qualified_shape_names (unchanged output), the prefix-composition order in the tuner matches create_input_data_channel, and the only resources.Tag consumers in sagemaker-serve use Tag.get_all (unaffected by the annotation change). The tests are thorough (AST guard, type-hint resolution across all tagged methods, codegen qualification, and tuner bucket/prefix). I found no issues worth blocking on; the PR looks clean.

Reviewed commit 23144e7. Automated review; verify before acting.

@jam-jee
jam-jee merged commit c869179 into aws:master Nov 12, 2026
28 of 30 checks passed

This branch was successfully deployed

1 active deployment
auto-approve — 23144e70 Deployed Nov 12, 2026 by jam-jee via wait-for-approval #1948
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HyperparameterTuner uploads local source without the ModelTrainer KMS key HyperParameterTuningJob.create rejects tuner tags on Python 3.14

2 participants