Repository navigation
fix: close the remaining gaps in optional torch support - #6531
Conversation
aws#6512 made the sagemaker-serve serving backends extras, but four gaps kept torch from being genuinely optional (aws#5531). All four were diagnosed first in aws#6166. Selecting the PyTorch default serializer still required torch. ``TorchTensorSerializer.__init__`` imported torch, so the lazy registry only deferred the failure to ``_fetch_serializer_and_deserializer_for_framework``. A client deploying a prebuilt PyTorch container detects the framework from the image URI and never touches a tensor, so it hit an ImportError for a dependency it had no use for. The type check now consults ``sys.modules``: a torch.Tensor cannot exist unless torch has already been imported, so the check stays exact without triggering an import. ``torch_tensor`` becomes a property, keeping the attribute and its ImportError for callers that want the type itself. Membership tests built the pair they were asking about. The registry inherited ``Mapping.__contains__``, which answers by calling ``__getitem__`` - defeating the laziness and raising a backend's ImportError out of an ``in`` test rather than returning True. ``TorchTensorTranslator`` raised a bare ModuleNotFoundError, leaving the caller to guess which extra to install. ``pip install sagemaker[torch]`` did not work. The top-level package now passes sagemaker-serve's backend extras through. ``all`` is unchanged, so no existing install starts pulling torch. Co-authored-by: goelakash <goelakash93@gmail.com>
There was a problem hiding this comment.
🤖 AI code review
This PR closes remaining gaps in optional torch support: TorchTensorSerializer no longer imports torch at construction (the type check now consults sys.modules and torch_tensor becomes a lazy property), _LazySerializerRegistry gets a __contains__ that doesn't build pairs, TorchTensorTranslator raises a clearer ImportError naming the extra, and the top-level sagemaker package forwards sagemaker-serve's backend extras. The change is well-targeted, consistent with the established optional-dependency approach (PR #5713/#5714), and comes with thorough subprocess-based tests that block torch via a meta_path hook. Overall it looks correct; only a minor consistency note below.
Reviewed commit c04b8a4. Automated review; verify before acting.
| raise ValueError(f"Object of type {type(data)} is not String serializable.") | ||
|
|
||
|
|
||
| def _is_torch_tensor(data): |
There was a problem hiding this comment.
🟡 Low: _is_torch_tensor here uses the sys.modules approach, while sagemaker.serve.builder.schema_builder._is_torch_tensor (already in the tree) still does from torch import Tensor inside a try/except. The two helpers now have the same name but different behaviour (one can trigger a torch import, the other never does). Worth a brief comment or aligning the serve-side helper so future readers don't assume they're equivalent.
Follow-up to #6512, which made the
sagemaker-serveserving backends extras.Credit where it's due: all four of these gaps were diagnosed first by @goelakash in #6166, which has been open since August. That PR was overtaken by #6512 and now conflicts with it in four places where #6512's version is the broader fix (
_LazySerializerRegistrykeepsDEFAULT_SERIALIZERS_BY_FRAMEWORK[x]returning instances, so no call sites change; it also covers onnx, onnxruntime, tritonclient and mlflow). This PR carries forward the part of #6166 that #6512 did not cover, rebased onto master. @goelakash is credited as co-author on the commit.What was still broken
Selecting the PyTorch default serializer still required torch.
TorchTensorSerializer.__init__imported torch, so_LazySerializerRegistryonly deferred the failure to_fetch_serializer_and_deserializer_for_framework. A client deploying a prebuilt PyTorch container detects the framework from the image URI and never handles a tensor, so it hit anImportErrorfor a dependency it had no use for — exactly the API-only case #5531 is about.The type check now consults
sys.modules:A
torch.Tensorinstance cannot exist unless torch has already been imported, so this is exactly as strict as theisinstancecheck it replaces — no duck-typing, no widening — while never triggering an import.torch_tensorbecomes a property, preserving the attribute and itsImportErrorfor callers that want the type itself.Membership tests built the pair they were asking about. The registry inherited
Mapping.__contains__, which is implemented astry: self[key] except KeyError: return False. Soframework_enum in DEFAULT_SERIALIZERS_BY_FRAMEWORKinstantiated the pair as a side effect, defeating the laziness, and forPYTORCHwithout torch raisedImportErrorout of a membership test instead of returningTrue.TorchTensorTranslatorraised a bareModuleNotFoundError, leaving the caller to guess which extra to install.pip install sagemaker[torch]did not work. The top-level package now passessagemaker-serve's backend extras (torch,onnx,triton,mlflow) through.allis deliberately unchanged, so no existing install starts pulling torch.Testing
masterand pass here.sagemaker-coreandsagemaker-serveunit tests matchingserial|deserial|torch|translator|constants: 374 passed. The only failures are 5 pre-existingTestTensorflowTensorTranslatorerrors (No module named 'tensorflow'), identical onmaster.meta_pathhook:_fetch_serializer_and_deserializer_for_framework("pytorch")returns(TorchTensorSerializer, JSONDeserializer)with"torch" not in sys.modules.black==26.3.1,flake8,pylint(10.00/10) andpydocstyleare clean on every changed file.Open question for reviewers
sagemaker[all]is left as the three subpackages, so it stays torch-free. The conventional reading ofallis "all optional functionality", which would make itsagemaker-serve[all]— but that silently re-adds the full backend set for anyone already usingsagemaker[all]. Flagging rather than deciding; happy to change it.