Skip to content

Commit 20a1c8e

Browse files
sobolevnFidget-SpinnerJelleZijlstra
authored
bpo-46195: Do not add Optional in get_type_hints (GH-30304)
Co-authored-by: Ken Jin <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 6ddb09f commit 20a1c8e

File tree

4 files changed

+26
-34
lines changed

4 files changed

+26
-34
lines changed

Diff for: Doc/library/typing.rst

+6-3
Original file line numberDiff line numberDiff line change
@@ -2185,9 +2185,7 @@ Introspection helpers
21852185

21862186
This is often the same as ``obj.__annotations__``. In addition,
21872187
forward references encoded as string literals are handled by evaluating
2188-
them in ``globals`` and ``locals`` namespaces. If necessary,
2189-
``Optional[t]`` is added for function and method annotations if a default
2190-
value equal to ``None`` is set. For a class ``C``, return
2188+
them in ``globals`` and ``locals`` namespaces. For a class ``C``, return
21912189
a dictionary constructed by merging all the ``__annotations__`` along
21922190
``C.__mro__`` in reverse order.
21932191

@@ -2214,6 +2212,11 @@ Introspection helpers
22142212
.. versionchanged:: 3.9
22152213
Added ``include_extras`` parameter as part of :pep:`593`.
22162214

2215+
.. versionchanged:: 3.11
2216+
Previously, ``Optional[t]`` was added for function and method annotations
2217+
if a default value equal to ``None`` was set.
2218+
Now the annotation is returned unchanged.
2219+
22172220
.. function:: get_args(tp)
22182221
.. function:: get_origin(tp)
22192222

Diff for: Lib/test/test_typing.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -2828,16 +2828,15 @@ def add_right(self, node: 'Node[T]' = None):
28282828
t = Node[int]
28292829
both_hints = get_type_hints(t.add_both, globals(), locals())
28302830
self.assertEqual(both_hints['left'], Optional[Node[T]])
2831-
self.assertEqual(both_hints['right'], Optional[Node[T]])
2832-
self.assertEqual(both_hints['left'], both_hints['right'])
2833-
self.assertEqual(both_hints['stuff'], Optional[int])
2831+
self.assertEqual(both_hints['right'], Node[T])
2832+
self.assertEqual(both_hints['stuff'], int)
28342833
self.assertNotIn('blah', both_hints)
28352834

28362835
left_hints = get_type_hints(t.add_left, globals(), locals())
28372836
self.assertEqual(left_hints['node'], Optional[Node[T]])
28382837

28392838
right_hints = get_type_hints(t.add_right, globals(), locals())
2840-
self.assertEqual(right_hints['node'], Optional[Node[T]])
2839+
self.assertEqual(right_hints['node'], Node[T])
28412840

28422841
def test_forwardref_instance_type_error(self):
28432842
fr = typing.ForwardRef('int')
@@ -3630,6 +3629,18 @@ def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]":
36303629
{'other': MySet[T], 'return': MySet[T]}
36313630
)
36323631

3632+
def test_get_type_hints_annotated_with_none_default(self):
3633+
# See: https://bugs.python.org/issue46195
3634+
def annotated_with_none_default(x: Annotated[int, 'data'] = None): ...
3635+
self.assertEqual(
3636+
get_type_hints(annotated_with_none_default),
3637+
{'x': int},
3638+
)
3639+
self.assertEqual(
3640+
get_type_hints(annotated_with_none_default, include_extras=True),
3641+
{'x': Annotated[int, 'data']},
3642+
)
3643+
36333644
def test_get_type_hints_classes_str_annotations(self):
36343645
class Foo:
36353646
y = str

Diff for: Lib/typing.py

+2-27
Original file line numberDiff line numberDiff line change
@@ -1879,26 +1879,6 @@ def cast(typ, val):
18791879
return val
18801880

18811881

1882-
def _get_defaults(func):
1883-
"""Internal helper to extract the default arguments, by name."""
1884-
try:
1885-
code = func.__code__
1886-
except AttributeError:
1887-
# Some built-in functions don't have __code__, __defaults__, etc.
1888-
return {}
1889-
pos_count = code.co_argcount
1890-
arg_names = code.co_varnames
1891-
arg_names = arg_names[:pos_count]
1892-
defaults = func.__defaults__ or ()
1893-
kwdefaults = func.__kwdefaults__
1894-
res = dict(kwdefaults) if kwdefaults else {}
1895-
pos_offset = pos_count - len(defaults)
1896-
for name, value in zip(arg_names[pos_offset:], defaults):
1897-
assert name not in res
1898-
res[name] = value
1899-
return res
1900-
1901-
19021882
_allowed_types = (types.FunctionType, types.BuiltinFunctionType,
19031883
types.MethodType, types.ModuleType,
19041884
WrapperDescriptorType, MethodWrapperType, MethodDescriptorType)
@@ -1908,8 +1888,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
19081888
"""Return type hints for an object.
19091889
19101890
This is often the same as obj.__annotations__, but it handles
1911-
forward references encoded as string literals, adds Optional[t] if a
1912-
default value equal to None is set and recursively replaces all
1891+
forward references encoded as string literals and recursively replaces all
19131892
'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
19141893
19151894
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):
19891968
else:
19901969
raise TypeError('{!r} is not a module, class, method, '
19911970
'or function.'.format(obj))
1992-
defaults = _get_defaults(obj)
19931971
hints = dict(hints)
19941972
for name, value in hints.items():
19951973
if value is None:
@@ -2002,10 +1980,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
20021980
is_argument=not isinstance(obj, types.ModuleType),
20031981
is_class=False,
20041982
)
2005-
value = _eval_type(value, globalns, localns)
2006-
if name in defaults and defaults[name] is None:
2007-
value = Optional[value]
2008-
hints[name] = value
1983+
hints[name] = _eval_type(value, globalns, localns)
20091984
return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()}
20101985

20111986

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`typing.get_type_hints` no longer adds ``Optional`` to parameters with
2+
``None`` as a default. This aligns to changes to PEP 484 in
3+
https://github.com/python/peps/pull/689

0 commit comments

Comments
 (0)