diff --git a/CHANGELOG.md b/CHANGELOG.md index 07fc328..a545e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/doc/index.rst b/doc/index.rst index 63082dd..bdf94c7 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -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 diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 79c1b88..d48880f 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -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 diff --git a/src/typing_extensions.py b/src/typing_extensions.py index f3132ea..4499c61 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -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__)