Skip to content

Commit

Permalink
Fixed checks being skipped for non-forward-reference NotRequired
Browse files Browse the repository at this point in the history
Fixes #454.
  • Loading branch information
agronholm committed Nov 3, 2024
1 parent 7ff5a51 commit 05a6772
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 5 deletions.
3 changes: 3 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/agronholm/typeguard/issues/467>`_)
- Fixed checks against annotations wrapped in ``NotRequired`` not being run unless the
``NotRequired`` is a forward reference
(`#454 <https://github.com/agronholm/typeguard/issues/454>`_)

**4.4.0** (2024-10-27)

Expand Down
7 changes: 4 additions & 3 deletions src/typeguard/_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 9 additions & 2 deletions tests/test_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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):
Expand Down

0 comments on commit 05a6772

Please sign in to comment.