-
-
Notifications
You must be signed in to change notification settings - Fork 117
Fix Union[..., NoneType]
injection by get_type_hints
if a None
default value is used.
#482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
376ae56
24c5602
7f000b1
8cc4464
881ffee
58082b9
5c94bd6
b42e203
ed552e4
313ddd8
3ba4ee7
6ffcd23
403e7ce
52c93e9
a1777f3
9e59796
1761a43
54b8eb0
5612f6f
91075ff
1eac721
1f84c68
214dfef
00dedc2
e929085
47e47ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1236,10 +1236,75 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): | |
) | ||
else: # 3.8 | ||
hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) | ||
if sys.version_info < (3, 11) and hint: | ||
hint = _clean_optional(obj, hint, globalns, localns) | ||
if include_extras: | ||
return hint | ||
return {k: _strip_extras(t) for k, t in hint.items()} | ||
|
||
_NoneType = type(None) | ||
|
||
def _could_be_inserted_optional(t): | ||
"""detects Union[..., None] pattern""" | ||
JelleZijlstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# 3.8+ compatible checking before _UnionGenericAlias | ||
if not hasattr(t, "__origin__") or t.__origin__ is not Union: | ||
return False | ||
# Assume if last argument is not None they are user defined | ||
if t.__args__[-1] is not _NoneType: | ||
return False | ||
return True | ||
|
||
# < 3.11 | ||
def _clean_optional(obj, hints, globalns=None, localns=None): | ||
# reverts injected Union[..., None] cases from typing.get_type_hints | ||
# when a None default value is used. | ||
# see https://github.com/python/typing_extensions/issues/310 | ||
original_hints = getattr(obj, '__annotations__', None) | ||
defaults = typing._get_defaults(obj) | ||
JelleZijlstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for name, value in hints.items(): | ||
# Not a Union[..., None] or replacement conditions not fullfilled | ||
if (not _could_be_inserted_optional(value) | ||
or name not in defaults | ||
or defaults[name] is not None | ||
): | ||
continue | ||
original_value = original_hints[name] | ||
if original_value is None: | ||
original_value = _NoneType | ||
# Forward reference | ||
if isinstance(original_value, str): | ||
if globalns is None: | ||
if isinstance(obj, _types.ModuleType): | ||
globalns = obj.__dict__ | ||
else: | ||
nsobj = obj | ||
# Find globalns for the unwrapped object. | ||
while hasattr(nsobj, '__wrapped__'): | ||
nsobj = nsobj.__wrapped__ | ||
globalns = getattr(nsobj, '__globals__', {}) | ||
if localns is None: | ||
localns = globalns | ||
elif localns is None: | ||
localns = globalns | ||
if sys.version_info < (3, 9): | ||
ref = ForwardRef(original_value) | ||
else: | ||
ref = ForwardRef( | ||
original_value, | ||
is_argument=not isinstance(obj, _types.ModuleType) | ||
) | ||
original_value = typing._eval_type(ref, globalns, localns) | ||
# Values was not modified or original is already Optional | ||
if original_value == value or _could_be_inserted_optional(original_value): | ||
continue | ||
# NoneType was added to value | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the feedback. Yes, it should have been piped trough |
||
if len(value.__args__) == 2: | ||
hints[name] = value.__args__[0] # not a Union | ||
Daraan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else: | ||
hints[name] = Union[value.__args__[:-1]] # still a Union | ||
|
||
return hints | ||
|
||
|
||
# Python 3.9+ has PEP 593 (Annotated) | ||
if hasattr(typing, 'Annotated'): | ||
|
Uh oh!
There was an error while loading. Please reload this page.