Skip to content

Commit

Permalink
Make sure that ReadOnly is removed when using `get_type_hints(inclu…
Browse files Browse the repository at this point in the history
…de_extra=False)` (#349)
  • Loading branch information
sobolevn committed Mar 6, 2024
1 parent 2d74216 commit c3dc681
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Release 4.11.0 (WIP)

- When `include_extra=False`, `get_type_hints()` now strips `ReadOnly` from the annotation.

# Release 4.10.0 (February 24, 2024)

This feature release adds support for PEP 728 (TypedDict with extra
Expand Down
5 changes: 5 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,11 @@ Functions

Interaction with :data:`Required` and :data:`NotRequired`.

.. versionchanged:: 4.11.0

When ``include_extra=False``, ``get_type_hints()`` now strips
:data:`ReadOnly` from the annotation.

.. function:: is_protocol(tp)

Determine if a type is a :class:`Protocol`. This works with protocols
Expand Down
14 changes: 14 additions & 0 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4199,6 +4199,20 @@ class AllTheThings(TypedDict):
self.assertEqual(AllTheThings.__readonly_keys__, frozenset({'a', 'b', 'c'}))
self.assertEqual(AllTheThings.__mutable_keys__, frozenset({'d'}))

self.assertEqual(
get_type_hints(AllTheThings, include_extras=False),
{'a': int, 'b': int, 'c': int, 'd': int},
)
self.assertEqual(
get_type_hints(AllTheThings, include_extras=True),
{
'a': Annotated[Required[ReadOnly[int]], 'why not'],
'b': Required[Annotated[ReadOnly[int], 'why not']],
'c': ReadOnly[NotRequired[Annotated[int, 'why not']]],
'd': NotRequired[Annotated[int, 'why not']],
},
)

def test_extra_keys_non_readonly(self):
class Base(TypedDict, closed=True):
__extra_items__: str
Expand Down
6 changes: 3 additions & 3 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1122,15 +1122,15 @@ def greet(name: str) -> None:
return val


if hasattr(typing, "Required"): # 3.11+
if hasattr(typing, "ReadOnly"): # 3.13+
get_type_hints = typing.get_type_hints
else: # <=3.10
else: # <=3.13
# replaces _strip_annotations()
def _strip_extras(t):
"""Strips Annotated, Required and NotRequired from a given type."""
if isinstance(t, _AnnotatedAlias):
return _strip_extras(t.__origin__)
if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly):
return _strip_extras(t.__args__[0])
if isinstance(t, typing._GenericAlias):
stripped_args = tuple(_strip_extras(a) for a in t.__args__)
Expand Down

0 comments on commit c3dc681

Please sign in to comment.