mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
bpo-42904: Change search order of typing.get_type_hints eval (#25632)
While surprising (searching globals before locals in one specific case), this is needed for backwards compatibility.
This commit is contained in:
parent
94549ee728
commit
1b1f9852bd
3 changed files with 17 additions and 3 deletions
|
@ -3016,10 +3016,10 @@ class GetTypeHintTests(BaseTestCase):
|
||||||
{'other': MySet[T], 'return': MySet[T]}
|
{'other': MySet[T], 'return': MySet[T]}
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_get_type_hints_classes(self):
|
def test_get_type_hints_classes_str_annotations(self):
|
||||||
class Foo:
|
class Foo:
|
||||||
y = str
|
y = str
|
||||||
x: y
|
x: 'y'
|
||||||
# This previously raised an error under PEP 563.
|
# This previously raised an error under PEP 563.
|
||||||
self.assertEqual(get_type_hints(Foo), {'x': str})
|
self.assertEqual(get_type_hints(Foo), {'x': str})
|
||||||
|
|
||||||
|
|
|
@ -1604,7 +1604,8 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
|
||||||
- If no dict arguments are passed, an attempt is made to use the
|
- If no dict arguments are passed, an attempt is made to use the
|
||||||
globals from obj (or the respective module's globals for classes),
|
globals from obj (or the respective module's globals for classes),
|
||||||
and these are also used as the locals. If the object does not appear
|
and these are also used as the locals. If the object does not appear
|
||||||
to have globals, an empty dictionary is used.
|
to have globals, an empty dictionary is used. For classes, the search
|
||||||
|
order is globals first then locals.
|
||||||
|
|
||||||
- If one dict argument is passed, it is used for both globals and
|
- If one dict argument is passed, it is used for both globals and
|
||||||
locals.
|
locals.
|
||||||
|
@ -1628,6 +1629,14 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
|
||||||
base_globals = globalns
|
base_globals = globalns
|
||||||
ann = base.__dict__.get('__annotations__', {})
|
ann = base.__dict__.get('__annotations__', {})
|
||||||
base_locals = dict(vars(base)) if localns is None else localns
|
base_locals = dict(vars(base)) if localns is None else localns
|
||||||
|
if localns is None and globalns is None:
|
||||||
|
# This is surprising, but required. Before Python 3.10,
|
||||||
|
# get_type_hints only evaluated the globalns of
|
||||||
|
# a class. To maintain backwards compatibility, we reverse
|
||||||
|
# the globalns and localns order so that eval() looks into
|
||||||
|
# *base_globals* first rather than *base_locals*.
|
||||||
|
# This only affects ForwardRefs.
|
||||||
|
base_globals, base_locals = base_locals, base_globals
|
||||||
for name, value in ann.items():
|
for name, value in ann.items():
|
||||||
if value is None:
|
if value is None:
|
||||||
value = type(None)
|
value = type(None)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
For backwards compatbility with previous minor versions of Python,
|
||||||
|
if :func:`typing.get_type_hints` receives no namespace dictionary arguments,
|
||||||
|
:func:`typing.get_type_hints` will search through the global then local
|
||||||
|
namespaces during evaluation of stringized type annotations
|
||||||
|
(string forward references) inside a class.
|
Loading…
Add table
Add a link
Reference in a new issue