Skip to content

Commit a1c1293

Browse files
committed
SQ: Fix test on Python 3.14+
1 parent 191393b commit a1c1293

File tree

2 files changed

+72
-25
lines changed

2 files changed

+72
-25
lines changed

tests.py

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,30 +2122,60 @@ class MaybePoint1D(mypy_extensions.TypedDict, total=False): # type: ignore[repo
21222122

21232123
class TaggedMaybePoint1D(MaybePoint1D):
21242124
name: str
2125-
2126-
self.assertTryCastSuccess(Point2D, {"x": 1, "y": 2}, strict=False)
2127-
self.assertTryCastFailure(Point2D, {"x": 1, "y": "string"}, strict=False)
2128-
self.assertTryCastFailure(Point2D, {"x": 1}, strict=False)
2129-
2130-
self.assertTryCastSuccess(Point3D, {"x": 1, "y": 2, "z": 3}, strict=False)
2131-
self.assertTryCastFailure(
2132-
Point3D, {"x": 1, "y": 2, "z": "string"}, strict=False
2133-
)
2134-
self.assertTryCastSuccess(Point3D, {"x": 1, "y": 2}, strict=False)
2135-
self.assertTryCastSuccess(Point3D, {"x": 1}, strict=False) # surprise!
2136-
self.assertTryCastSuccess(Point3D, {"q": 1}, strict=False) # surprise!
2137-
2138-
self.assertTryCastSuccess(MaybePoint1D, {"x": 1}, strict=False)
2139-
self.assertTryCastFailure(MaybePoint1D, {"x": "string"}, strict=False)
2140-
self.assertTryCastSuccess(MaybePoint1D, {}, strict=False)
2141-
self.assertTryCastSuccess(MaybePoint1D, {"q": 1}, strict=False) # surprise!
2142-
2143-
self.assertTryCastSuccess(
2144-
TaggedMaybePoint1D, {"x": 1, "name": "one"}, strict=False
2145-
)
2146-
self.assertTryCastFailure(
2147-
TaggedMaybePoint1D, {"name": "one"}, strict=False
2148-
) # surprise!
2125+
2126+
@contextmanager
2127+
def assert_raises_if_python_3_14_or_later() -> Iterator[None]:
2128+
try:
2129+
yield
2130+
except TypeNotSupportedError as e:
2131+
if 'cannot determine which keys exist on a mypy_extensions.TypedDict' in str(e):
2132+
if sys.version_info[:2] >= (3, 14):
2133+
pass # expected
2134+
else:
2135+
raise
2136+
else:
2137+
raise
2138+
else:
2139+
if sys.version_info[:2] >= (3, 14):
2140+
raise AssertionError('Expected TypeNotSupportedError to be raised')
2141+
2142+
with assert_raises_if_python_3_14_or_later():
2143+
self.assertTryCastSuccess(Point2D, {"x": 1, "y": 2}, strict=False)
2144+
with assert_raises_if_python_3_14_or_later():
2145+
self.assertTryCastFailure(Point2D, {"x": 1, "y": "string"}, strict=False)
2146+
with assert_raises_if_python_3_14_or_later():
2147+
self.assertTryCastFailure(Point2D, {"x": 1}, strict=False)
2148+
2149+
with assert_raises_if_python_3_14_or_later():
2150+
self.assertTryCastSuccess(Point3D, {"x": 1, "y": 2, "z": 3}, strict=False)
2151+
with assert_raises_if_python_3_14_or_later():
2152+
self.assertTryCastFailure(
2153+
Point3D, {"x": 1, "y": 2, "z": "string"}, strict=False
2154+
)
2155+
with assert_raises_if_python_3_14_or_later():
2156+
self.assertTryCastSuccess(Point3D, {"x": 1, "y": 2}, strict=False)
2157+
with assert_raises_if_python_3_14_or_later():
2158+
self.assertTryCastSuccess(Point3D, {"x": 1}, strict=False) # surprise!
2159+
with assert_raises_if_python_3_14_or_later():
2160+
self.assertTryCastSuccess(Point3D, {"q": 1}, strict=False) # surprise!
2161+
2162+
with assert_raises_if_python_3_14_or_later():
2163+
self.assertTryCastSuccess(MaybePoint1D, {"x": 1}, strict=False)
2164+
with assert_raises_if_python_3_14_or_later():
2165+
self.assertTryCastFailure(MaybePoint1D, {"x": "string"}, strict=False)
2166+
with assert_raises_if_python_3_14_or_later():
2167+
self.assertTryCastSuccess(MaybePoint1D, {}, strict=False)
2168+
with assert_raises_if_python_3_14_or_later():
2169+
self.assertTryCastSuccess(MaybePoint1D, {"q": 1}, strict=False) # surprise!
2170+
2171+
with assert_raises_if_python_3_14_or_later():
2172+
self.assertTryCastSuccess(
2173+
TaggedMaybePoint1D, {"x": 1, "name": "one"}, strict=False
2174+
)
2175+
with assert_raises_if_python_3_14_or_later():
2176+
self.assertTryCastFailure(
2177+
TaggedMaybePoint1D, {"name": "one"}, strict=False
2178+
) # surprise!
21492179

21502180
# NOTE: Cannot combine the following two if-checks with an `and`
21512181
# because that is too complicated for Pyre to understand.

trycast/__init__.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def __class_getitem__(cls, key):
147147

148148
_typed_dict_meta_list.append(_MypyExtensionsTypedDictMeta) # type: ignore[16] # pyre
149149
except ImportError:
150-
pass
150+
_MypyExtensionsTypedDictMeta = None
151151

152152
_typed_dict_metas = tuple(_typed_dict_meta_list)
153153

@@ -156,6 +156,13 @@ def _is_typed_dict(tp: object) -> bool:
156156
return isinstance(tp, _typed_dict_metas)
157157

158158

159+
def _is_mypy_extensions_typed_dict(tp: object) -> bool:
160+
return (
161+
_MypyExtensionsTypedDictMeta is not None and
162+
isinstance(tp, _MypyExtensionsTypedDictMeta)
163+
)
164+
165+
159166
# _is_newtype
160167
if NewType.__class__.__name__ == "function": # type: ignore[reportGeneralTypeIssues] # pyright
161168
# Python 3.8 - 3.9
@@ -858,6 +865,16 @@ def _checkcast_typeddict(
858865
) # resolve ForwardRefs in typed_dict_class.__annotations__
859866
else:
860867
resolved_annotations = typed_dict_class.__annotations__ # type: ignore[attr-defined] # mypy
868+
if (resolved_annotations == {} and
869+
_is_mypy_extensions_typed_dict(typed_dict_class) and
870+
sys.version_info[:2] >= (3, 14)):
871+
# NOTE: This is probably a bug in mypy_extensions.TypedDict or Python 3.14+.
872+
# But it is unlikely to be fixed because mypy_extensions.TypedDict is
873+
# deprecated.
874+
raise TypeNotSupportedError(
875+
f"{options.funcname} cannot determine which keys exist on a "
876+
f"mypy_extensions.TypedDict in Python 3.14+"
877+
)
861878

862879
try:
863880
# {typing in Python 3.9+, typing_extensions}.TypedDict

0 commit comments

Comments
 (0)