Repository navigation
fix: Python 3.14 tuner tags (#6554) and tuner source staging bucket (#6555) - #6578
Conversation
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
There was a problem hiding this comment.
🤖 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
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
There was a problem hiding this comment.
🤖 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.
Two fixes reported on the same customer case. One commit for each issue.
#6554:
HyperParameterTuningJob.createrejects tuner tags on Python 3.14Root cause.
resources.pyrunsfrom sagemaker.core.shapes import *and later defines the resource classTag(the ListTags resource). Annotations written above that class, such asHyperParameterTuningJob.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 toresources.Tag. Pydantic then rejects theshapes.Tagobjects thatHyperparameterTunersends.Annotations written below the class already resolved to
resources.Tagon every version. That affectsTrainingJob.create,TransformJob.create,Trial.create,TrainingPlan.create,TrainingSession.create, the otherT*/U*/W*create()methods andTag.add_tags. It is whyModelTrainerhas to pass dicts.Fix.
RESOURCE_SHADOWED_SHAPE_NAMES(onlyTag) asshapes.<Name>, and addsfrom sagemaker.core import shapesto the generated imports (tools/constants.py,tools/shapes_extractor.py,tools/resources_codegen.py).resources.pyis regenerated with the codegen. Before the change, regenerating master reproducedresources.pybyte for byte, so this diff comes only from this change: 75tagsannotations plus the import.tagsparameter now takes ashapes.Tagor a dict on every Python version.Behavior change. The methods listed above, defined after
class Tag, used to acceptresources.Taginstances and rejectshapes.Tag. Now they do the opposite. Nothing in the SDK passesresources.Tagto them; it is only used forTag.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:create/add_tagsmethods:get_type_hintsresolvestagsto a type that validatesshapes.Tagand dicts intoshapes.Tag.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.gzupload. The upload still went tosession.default_bucket()with nodefault_bucket_prefix.ModelTrainer.create_input_data_channelstagessm_driversfor 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 addsdefault_bucket_prefixto the key. The source archive now follows the same rules, so it lands next tosm_driversand 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 fordefault_bucket_prefix.Verification
sagemaker-core4106 passed,sagemaker-train3509 passed.python:3.14-slimcontainer):HyperParameterTuningJob.createrejects tuner tags on Python 3.14 #6554 repro now validatesshapes.TagforHyperParameterTuningJob.createandTrainingJob.create.sagemaker-serveunit tests and live AWS calls.Closes #6554
Closes #6555