diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 902d875..af93517 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -6,6 +6,8 @@ This library adheres to **UNRELEASED** +- Fixed some compatibility problems when running on Python 3.13 + (`#460 `_) - Fixed test suite incompatibility with pytest 8.2 (`#461 `_) diff --git a/pyproject.toml b/pyproject.toml index 495312c..d427b58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ requires-python = ">= 3.8" dependencies = [ "importlib_metadata >= 3.6; python_version < '3.10'", - "typing_extensions >= 4.10.0; python_version < '3.13'", + "typing_extensions >= 4.10.0", ] dynamic = ["version"] diff --git a/src/typeguard/_checkers.py b/src/typeguard/_checkers.py index 2f8de6f..0203f4e 100644 --- a/src/typeguard/_checkers.py +++ b/src/typeguard/_checkers.py @@ -38,16 +38,17 @@ except ImportError: typing_extensions = None # type: ignore[assignment] +# Must use this because typing.is_typeddict does not recognize +# TypedDict from typing_extensions, and as of version 4.12.0 +# typing_extensions.TypedDict is different from typing.TypedDict +# on all versions. +from typing_extensions import is_typeddict + from ._config import ForwardRefPolicy from ._exceptions import TypeCheckError, TypeHintWarning from ._memo import TypeCheckMemo from ._utils import evaluate_forwardref, get_stacklevel, get_type_name, qualified_name -if sys.version_info >= (3, 13): - from typing import is_typeddict -else: - from typing_extensions import is_typeddict - if sys.version_info >= (3, 11): from typing import ( Annotated, diff --git a/src/typeguard/_utils.py b/src/typeguard/_utils.py index 96818fd..9bcc841 100644 --- a/src/typeguard/_utils.py +++ b/src/typeguard/_utils.py @@ -11,11 +11,21 @@ if TYPE_CHECKING: from ._memo import TypeCheckMemo -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 13): from typing import get_args, get_origin def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any: - return forwardref._evaluate(memo.globals, memo.locals, frozenset()) + return forwardref._evaluate( + memo.globals, memo.locals, type_params=(), recursive_guard=frozenset() + ) + +elif sys.version_info >= (3, 10): + from typing import get_args, get_origin + + def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any: + return forwardref._evaluate( + memo.globals, memo.locals, recursive_guard=frozenset() + ) else: from typing_extensions import get_args, get_origin diff --git a/tests/test_checkers.py b/tests/test_checkers.py index 29b1b01..c3233d8 100644 --- a/tests/test_checkers.py +++ b/tests/test_checkers.py @@ -43,6 +43,7 @@ check_type_internal, suppress_type_checks, ) +from typeguard._checkers import is_typeddict from typeguard._utils import qualified_name from . import ( @@ -530,6 +531,14 @@ class DummyDict(typing_provider.TypedDict): ): check_type({"x": 1, "y": "foo"}, DummyDict) + def test_is_typeddict(self, typing_provider): + # Ensure both typing.TypedDict and typing_extensions.TypedDict are recognized + class DummyDict(typing_provider.TypedDict): + x: int + + assert is_typeddict(DummyDict) + assert not is_typeddict(dict) + class TestList: def test_bad_type(self):