diff options
-rw-r--r-- | Doc/library/typing.rst | 9 | ||||
-rw-r--r-- | Lib/test/test_typing.py | 19 | ||||
-rw-r--r-- | Lib/typing.py | 29 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst | 3 |
4 files changed, 26 insertions, 34 deletions
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index a02ad24..bfcbeb8 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2185,9 +2185,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. @@ -2214,6 +2212,11 @@ Introspection helpers .. versionchanged:: 3.9 Added ``include_extras`` parameter as part of :pep:`593`. + .. versionchanged:: 3.11 + 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) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index dc1514d..8fcc24c 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2828,16 +2828,15 @@ class ForwardRefTests(BaseTestCase): 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') @@ -3630,6 +3629,18 @@ class GetTypeHintTests(BaseTestCase): {'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 ad1435e..9d668b3 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1879,26 +1879,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) @@ -1908,8 +1888,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 @@ -1989,7 +1968,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: @@ -2002,10 +1980,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 0000000..03ea46c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-12-30-21-38-51.bpo-46195.jFKGq_.rst @@ -0,0 +1,3 @@ +: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 |