diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 554b8b6..0bba890 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -19,6 +19,9 @@ This library adheres to - Fixed ``Self`` checks in instance/class methods that have positional-only arguments - Fixed explicit checks of PEP 604 unions against ``types.UnionType`` (`#467 `_) +- Fixed checks against annotations wrapped in ``NotRequired`` not being run unless the + ``NotRequired`` is a forward reference + (`#454 `_) **4.4.0** (2024-10-27) diff --git a/src/typeguard/_checkers.py b/src/typeguard/_checkers.py index 40ff2d4..5e34036 100644 --- a/src/typeguard/_checkers.py +++ b/src/typeguard/_checkers.py @@ -263,9 +263,10 @@ def check_typed_dict( for key, annotation in origin_type.__annotations__.items(): if isinstance(annotation, ForwardRef): annotation = evaluate_forwardref(annotation, memo) - if get_origin(annotation) is NotRequired: - required_keys.discard(key) - annotation = get_args(annotation)[0] + + if get_origin(annotation) is NotRequired: + required_keys.discard(key) + annotation = get_args(annotation)[0] type_hints[key] = annotation diff --git a/tests/test_checkers.py b/tests/test_checkers.py index d03080b..8653535 100644 --- a/tests/test_checkers.py +++ b/tests/test_checkers.py @@ -508,7 +508,8 @@ def test_notrequired_pass(self, typing_provider): class DummyDict(typing_provider.TypedDict): x: int - y: "NotRequired[int]" + y: NotRequired[int] + z: "NotRequired[int]" check_type({"x": 8}, DummyDict) @@ -520,13 +521,19 @@ def test_notrequired_fail(self, typing_provider): class DummyDict(typing_provider.TypedDict): x: int - y: "NotRequired[int]" + y: NotRequired[int] + z: "NotRequired[int]" with pytest.raises( TypeCheckError, match=r"value of key 'y' of dict is not an instance of int" ): check_type({"x": 1, "y": "foo"}, DummyDict) + with pytest.raises( + TypeCheckError, match=r"value of key 'z' of dict is not an instance of int" + ): + check_type({"x": 1, "y": 6, "z": "foo"}, DummyDict) + def test_is_typeddict(self, typing_provider): # Ensure both typing.TypedDict and typing_extensions.TypedDict are recognized class DummyDict(typing_provider.TypedDict):