From dd3a750c0118cbc02307f2fc2a2854d2956aaeaf Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 30 Dec 2021 21:39:15 +0300 Subject: [PATCH 1/6] bpo-46195: `get_type_hints` does not add `Optional` anymore --- Doc/library/typing.rst | 4 +-- Lib/test/test_typing.py | 19 +++++++++--- Lib/typing.py | 29 ++----------------- .../2021-12-30-21-38-51.bpo-46195.jFKGq_.rst | 4 +++ 4 files changed, 22 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 08b59d84246f8d..c4f03ce2464bc3 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2030,9 +2030,7 @@ Introspection helpers This is often the same as ``obj.__annotations__``. In addition, forward references encoded as string literals are handled by evaluating - them in ``globals`` and ``locals`` namespaces. If necessary, - ``Optional[t]`` is added for function and method annotations if a default - value equal to ``None`` is set. For a class ``C``, return + them in ``globals`` and ``locals`` namespaces. For a class ``C``, return a dictionary constructed by merging all the ``__annotations__`` along ``C.__mro__`` in reverse order. diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index a94d77d4edf4be..79ebd69799a349 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2586,16 +2586,15 @@ def add_right(self, node: 'Node[T]' = None): t = Node[int] both_hints = get_type_hints(t.add_both, globals(), locals()) self.assertEqual(both_hints['left'], Optional[Node[T]]) - self.assertEqual(both_hints['right'], Optional[Node[T]]) - self.assertEqual(both_hints['left'], both_hints['right']) - self.assertEqual(both_hints['stuff'], Optional[int]) + self.assertEqual(both_hints['right'], Node[T]) + self.assertEqual(both_hints['stuff'], int) self.assertNotIn('blah', both_hints) left_hints = get_type_hints(t.add_left, globals(), locals()) self.assertEqual(left_hints['node'], Optional[Node[T]]) right_hints = get_type_hints(t.add_right, globals(), locals()) - self.assertEqual(right_hints['node'], Optional[Node[T]]) + self.assertEqual(right_hints['node'], Node[T]) def test_forwardref_instance_type_error(self): fr = typing.ForwardRef('int') @@ -3259,6 +3258,18 @@ def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]": {'other': MySet[T], 'return': MySet[T]} ) + def test_get_type_hints_annotated_with_none_default(self): + # See: https://bugs.python.org/issue46195 + def annotated_with_none_default(x: Annotated[int, 'data'] = None): ... + self.assertEqual( + get_type_hints(annotated_with_none_default), + {'x': int}, + ) + self.assertEqual( + get_type_hints(annotated_with_none_default, include_extras=True), + {'x': Annotated[int, 'data']}, + ) + def test_get_type_hints_classes_str_annotations(self): class Foo: y = str diff --git a/Lib/typing.py b/Lib/typing.py index ae1dd5c2d76891..6c3677462199ee 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1728,26 +1728,6 @@ def cast(typ, val): return val -def _get_defaults(func): - """Internal helper to extract the default arguments, by name.""" - try: - code = func.__code__ - except AttributeError: - # Some built-in functions don't have __code__, __defaults__, etc. - return {} - pos_count = code.co_argcount - arg_names = code.co_varnames - arg_names = arg_names[:pos_count] - defaults = func.__defaults__ or () - kwdefaults = func.__kwdefaults__ - res = dict(kwdefaults) if kwdefaults else {} - pos_offset = pos_count - len(defaults) - for name, value in zip(arg_names[pos_offset:], defaults): - assert name not in res - res[name] = value - return res - - _allowed_types = (types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.ModuleType, WrapperDescriptorType, MethodWrapperType, MethodDescriptorType) @@ -1757,8 +1737,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): """Return type hints for an object. This is often the same as obj.__annotations__, but it handles - forward references encoded as string literals, adds Optional[t] if a - default value equal to None is set and recursively replaces all + forward references encoded as string literals and recursively replaces all 'Annotated[T, ...]' with 'T' (unless 'include_extras=True'). The argument may be a module, class, method, or function. The annotations @@ -1838,7 +1817,6 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): else: raise TypeError('{!r} is not a module, class, method, ' 'or function.'.format(obj)) - defaults = _get_defaults(obj) hints = dict(hints) for name, value in hints.items(): if value is None: @@ -1851,10 +1829,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): is_argument=not isinstance(obj, types.ModuleType), is_class=False, ) - value = _eval_type(value, globalns, localns) - if name in defaults and defaults[name] is None: - value = Optional[value] - hints[name] = value + hints[name] = _eval_type(value, globalns, localns) return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()} diff --git a/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst b/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst new file mode 100644 index 00000000000000..b0e5ef261156ed --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst @@ -0,0 +1,4 @@ +Now :func:`typing.get_type_hints` does not add ``Optional`` to types with +explicit ``None`` default value. + +This was changed in https://github.com/python/peps/pull/689 From 1cbe416594a4214ce5e6e96d0d27c0efa1a31f3f Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 10 Jan 2022 12:16:29 +0300 Subject: [PATCH 2/6] Add `.. versionchanged` information --- Doc/library/typing.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index c4f03ce2464bc3..807a248af13496 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2056,6 +2056,10 @@ Introspection helpers .. versionchanged:: 3.9 Added ``include_extras`` parameter as part of :pep:`593`. + + .. versionchanged:: 3.11 + ``Optional`` annotation is not added implicitly anymore + when ``None`` default is used for a function argument. .. function:: get_args(tp) .. function:: get_origin(tp) From b87e7858ae08d579b082580474ec2a388df0ca0f Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Mon, 10 Jan 2022 12:23:38 +0300 Subject: [PATCH 3/6] Update typing.rst --- Doc/library/typing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 807a248af13496..93ae7d7a542a71 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2056,9 +2056,9 @@ Introspection helpers .. versionchanged:: 3.9 Added ``include_extras`` parameter as part of :pep:`593`. - + .. versionchanged:: 3.11 - ``Optional`` annotation is not added implicitly anymore + ``Optional`` annotation is not added implicitly anymore when ``None`` default is used for a function argument. .. function:: get_args(tp) From e4f5fee04886c3fbe7cbeb503ef69b5775e91878 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 11 Jan 2022 17:57:27 +0300 Subject: [PATCH 4/6] Update Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> --- .../next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst b/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst index b0e5ef261156ed..036f8475f3de19 100644 --- a/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst +++ b/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst @@ -1,4 +1,3 @@ Now :func:`typing.get_type_hints` does not add ``Optional`` to types with -explicit ``None`` default value. - -This was changed in https://github.com/python/peps/pull/689 +explicit ``None`` default value. This aligns to changes to PEP 484 in +https://github.com/python/peps/pull/689 From 396ddcf8c2e2a7a469c024fe7f9a2213c686bab9 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 22 Jan 2022 11:04:27 +0300 Subject: [PATCH 5/6] Change docs --- Doc/library/typing.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 93ae7d7a542a71..9012c5224eced5 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2058,8 +2058,9 @@ Introspection helpers Added ``include_extras`` parameter as part of :pep:`593`. .. versionchanged:: 3.11 - ``Optional`` annotation is not added implicitly anymore - when ``None`` default is used for a function argument. + Previously, ``Optional[t]`` was added for function and method annotations + if a default value equal to ``None`` was set. + Now the annotation is returned unchanged. .. function:: get_args(tp) .. function:: get_origin(tp) From 153dd0903a1b3412aebd45e392106e992575e301 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 17 Feb 2022 11:12:43 +0300 Subject: [PATCH 6/6] Update Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst Co-authored-by: Jelle Zijlstra --- .../next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst b/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst index 036f8475f3de19..03ea46c3a83f13 100644 --- a/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst +++ b/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst @@ -1,3 +1,3 @@ -Now :func:`typing.get_type_hints` does not add ``Optional`` to types with -explicit ``None`` default value. This aligns to changes to PEP 484 in +:func:`typing.get_type_hints` no longer adds ``Optional`` to parameters with +``None`` as a default. This aligns to changes to PEP 484 in https://github.com/python/peps/pull/689