Skip to content

Commit d08fca1

Browse files
committed
fix: [Bug]: PySDK V3: pydantic-core (2.42.0) incompatibility with sagemaker.ai_regist (5652)
1 parent 272fdbf commit d08fca1

File tree

4 files changed

+169
-1
lines changed

4 files changed

+169
-1
lines changed

sagemaker-core/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ readme = "README.rst"
1313
dependencies = [
1414
# Add your dependencies here (Include lower and upper bounds as applicable)
1515
"boto3>=1.42.2,<2.0.0",
16-
"pydantic>=2.0.0,<3.0.0",
16+
"pydantic>=2.10.0,<3.0.0",
17+
"pydantic-core>=2.27.0,<3.0.0",
1718
"PyYAML>=6.0, <7.0",
1819
"jsonschema<5.0.0",
1920
"platformdirs>=4.0.0, <5.0.0",

sagemaker-core/src/sagemaker/core/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# Early pydantic compatibility check - must happen before any pydantic imports
2+
try:
3+
from sagemaker.core._pydantic_compat import check_pydantic_compatibility
4+
check_pydantic_compatibility()
5+
except ImportError as e:
6+
if "pydantic" in str(e).lower() and ("incompatible" in str(e).lower() or "mismatch" in str(e).lower()):
7+
raise
8+
# If it's a different ImportError (e.g., pydantic not installed yet), let it pass
9+
# and fail later with a more standard error
10+
pass
11+
112
from sagemaker.core.utils.utils import enable_textual_rich_console_and_traceback
213

314

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
"""Pydantic compatibility check for sagemaker-core.
14+
15+
This module provides an early check for pydantic/pydantic-core version
16+
compatibility to give users a clear error message with fix instructions
17+
instead of a cryptic SystemError.
18+
"""
19+
20+
21+
def check_pydantic_compatibility():
22+
"""Check that pydantic and pydantic-core versions are compatible.
23+
24+
Raises:
25+
ImportError: If pydantic and pydantic-core versions are incompatible,
26+
with instructions on how to fix the issue.
27+
"""
28+
try:
29+
import pydantic # noqa: F401
30+
except SystemError as e:
31+
error_message = str(e)
32+
raise ImportError(
33+
f"Pydantic version incompatibility detected: {error_message}\n\n"
34+
"This typically happens when pydantic-core is upgraded independently "
35+
"of pydantic, causing a version mismatch.\n\n"
36+
"To fix this, run:\n"
37+
" pip install pydantic pydantic-core --force-reinstall\n\n"
38+
"This will ensure both packages are installed at compatible versions."
39+
) from e
40+
41+
try:
42+
import pydantic_core # noqa: F401
43+
except ImportError:
44+
# pydantic_core not installed separately is fine;
45+
# pydantic manages it as a dependency
46+
return
47+
48+
# Additional version check: pydantic declares the exact pydantic-core
49+
# version it requires. Verify they match.
50+
try:
51+
pydantic_version = pydantic.VERSION
52+
pydantic_core_version = pydantic_core.VERSION
53+
54+
# pydantic >= 2.x stores the required core version
55+
expected_core_version = getattr(pydantic, '__pydantic_core_version__', None)
56+
if expected_core_version is None:
57+
# Try alternative attribute name used in some pydantic versions
58+
expected_core_version = getattr(
59+
pydantic, '_internal', None
60+
) and getattr(
61+
getattr(pydantic, '_internal', None),
62+
'_generate_schema',
63+
None,
64+
)
65+
# If we can't determine the expected version, skip the check
66+
return
67+
68+
if pydantic_core_version != expected_core_version:
69+
raise ImportError(
70+
f"Pydantic/pydantic-core version mismatch detected: "
71+
f"pydantic {pydantic_version} requires pydantic-core=={expected_core_version}, "
72+
f"but pydantic-core {pydantic_core_version} is installed.\n\n"
73+
"To fix this, run:\n"
74+
" pip install pydantic pydantic-core --force-reinstall\n\n"
75+
"This will ensure both packages are installed at compatible versions."
76+
)
77+
except (AttributeError, TypeError):
78+
# If we can't determine versions, skip the check
79+
# The SystemError catch above will handle the most common case
80+
pass
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
"""Tests for pydantic compatibility check."""
14+
15+
import sys
16+
from unittest import mock
17+
18+
import pytest
19+
20+
21+
def test_check_pydantic_compatibility_passes_with_matching_versions():
22+
"""Verify the check function does not raise when pydantic and pydantic-core are compatible."""
23+
from sagemaker.core._pydantic_compat import check_pydantic_compatibility
24+
25+
# Should not raise any exception with the currently installed versions
26+
check_pydantic_compatibility()
27+
28+
29+
def test_check_pydantic_compatibility_raises_on_system_error():
30+
"""Mock pydantic import to raise SystemError and verify a clear ImportError is raised."""
31+
from sagemaker.core._pydantic_compat import check_pydantic_compatibility
32+
33+
error_msg = (
34+
"The installed pydantic-core version (2.42.0) is incompatible "
35+
"with the current pydantic version, which requires 2.41.5."
36+
)
37+
38+
with mock.patch.dict(sys.modules, {"pydantic": None}):
39+
original_import = __builtins__.__import__ if hasattr(__builtins__, '__import__') else __import__
40+
41+
def mock_import(name, *args, **kwargs):
42+
if name == "pydantic":
43+
raise SystemError(error_msg)
44+
return original_import(name, *args, **kwargs)
45+
46+
with mock.patch("builtins.__import__", side_effect=mock_import):
47+
with pytest.raises(ImportError) as exc_info:
48+
check_pydantic_compatibility()
49+
50+
assert "incompatibility detected" in str(exc_info.value).lower() or \
51+
"incompatible" in str(exc_info.value).lower()
52+
53+
54+
def test_pydantic_import_error_message_contains_instructions():
55+
"""Verify the error message includes pip install instructions."""
56+
from sagemaker.core._pydantic_compat import check_pydantic_compatibility
57+
58+
error_msg = (
59+
"The installed pydantic-core version (2.42.0) is incompatible "
60+
"with the current pydantic version, which requires 2.41.5."
61+
)
62+
63+
with mock.patch.dict(sys.modules, {"pydantic": None}):
64+
original_import = __builtins__.__import__ if hasattr(__builtins__, '__import__') else __import__
65+
66+
def mock_import(name, *args, **kwargs):
67+
if name == "pydantic":
68+
raise SystemError(error_msg)
69+
return original_import(name, *args, **kwargs)
70+
71+
with mock.patch("builtins.__import__", side_effect=mock_import):
72+
with pytest.raises(ImportError) as exc_info:
73+
check_pydantic_compatibility()
74+
75+
error_str = str(exc_info.value)
76+
assert "pip install pydantic pydantic-core --force-reinstall" in error_str

0 commit comments

Comments
 (0)