Skip to content

Commit

Permalink
Support Python 3.13 (#460)
Browse files Browse the repository at this point in the history
- typing.is_typeddict() does not recognize typing_extensions.TypedDict
- ForwardRef._evaluate gained another parameter
- Made typing_extensions an unconditional dependency
  • Loading branch information
JelleZijlstra authored May 25, 2024
1 parent dd3fc1e commit 6647e5d
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 8 deletions.
2 changes: 2 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This library adheres to

**UNRELEASED**

- Fixed some compatibility problems when running on Python 3.13
(`#460 <https://github.com/agronholm/typeguard/issues/460>`_)
- Fixed test suite incompatibility with pytest 8.2
(`#461 <https://github.com/agronholm/typeguard/issues/461>`_)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
11 changes: 6 additions & 5 deletions src/typeguard/_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 12 additions & 2 deletions src/typeguard/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions tests/test_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
check_type_internal,
suppress_type_checks,
)
from typeguard._checkers import is_typeddict
from typeguard._utils import qualified_name

from . import (
Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit 6647e5d

Please sign in to comment.