Skip to content

Commit 1b1f985

Browse files
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.
1 parent 94549ee commit 1b1f985

File tree

3 files changed

+17
-3
lines changed

3 files changed

+17
-3
lines changed

Lib/test/test_typing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3016,10 +3016,10 @@ def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]":
30163016
{'other': MySet[T], 'return': MySet[T]}
30173017
)
30183018

3019-
def test_get_type_hints_classes(self):
3019+
def test_get_type_hints_classes_str_annotations(self):
30203020
class Foo:
30213021
y = str
3022-
x: y
3022+
x: 'y'
30233023
# This previously raised an error under PEP 563.
30243024
self.assertEqual(get_type_hints(Foo), {'x': str})
30253025

Lib/typing.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1604,7 +1604,8 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
16041604
- If no dict arguments are passed, an attempt is made to use the
16051605
globals from obj (or the respective module's globals for classes),
16061606
and these are also used as the locals. If the object does not appear
1607-
to have globals, an empty dictionary is used.
1607+
to have globals, an empty dictionary is used. For classes, the search
1608+
order is globals first then locals.
16081609
16091610
- If one dict argument is passed, it is used for both globals and
16101611
locals.
@@ -1628,6 +1629,14 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
16281629
base_globals = globalns
16291630
ann = base.__dict__.get('__annotations__', {})
16301631
base_locals = dict(vars(base)) if localns is None else localns
1632+
if localns is None and globalns is None:
1633+
# This is surprising, but required. Before Python 3.10,
1634+
# get_type_hints only evaluated the globalns of
1635+
# a class. To maintain backwards compatibility, we reverse
1636+
# the globalns and localns order so that eval() looks into
1637+
# *base_globals* first rather than *base_locals*.
1638+
# This only affects ForwardRefs.
1639+
base_globals, base_locals = base_locals, base_globals
16311640
for name, value in ann.items():
16321641
if value is None:
16331642
value = type(None)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
For backwards compatbility with previous minor versions of Python,
2+
if :func:`typing.get_type_hints` receives no namespace dictionary arguments,
3+
:func:`typing.get_type_hints` will search through the global then local
4+
namespaces during evaluation of stringized type annotations
5+
(string forward references) inside a class.

0 commit comments

Comments
 (0)