mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
annotationlib: Move ForwardRef tests to test_annotationlib (#132571)
I started with just moving ForwardRefTests to test_annotationlib, but found that it contained a number of tests for no_type_check, which I moved to a new class in test_typing, as well as a number of tests that are more appropriately classified as tests for get_type_hints(). One test, test_forward_equality_namespace(), was somewhat accidentally depending on a global class A in test_typing. I added a class A in the annotationlib tests instead. Also add a useful comment in annotationlib.
This commit is contained in:
parent
10a77619f9
commit
7dcaebfb21
3 changed files with 320 additions and 314 deletions
|
@ -77,11 +77,15 @@ class ForwardRef:
|
|||
self.__forward_is_argument__ = is_argument
|
||||
self.__forward_is_class__ = is_class
|
||||
self.__forward_module__ = module
|
||||
self.__owner__ = owner
|
||||
# These are always set to None here but may be non-None if a ForwardRef
|
||||
# is created through __class__ assignment on a _Stringifier object.
|
||||
self.__globals__ = None
|
||||
self.__cell__ = None
|
||||
# These are initially None but serve as a cache and may be set to a non-None
|
||||
# value later.
|
||||
self.__code__ = None
|
||||
self.__ast_node__ = None
|
||||
self.__cell__ = None
|
||||
self.__owner__ = owner
|
||||
|
||||
def __init_subclass__(cls, /, *args, **kwds):
|
||||
raise TypeError("Cannot subclass ForwardRef")
|
||||
|
|
|
@ -6,6 +6,7 @@ import collections
|
|||
import functools
|
||||
import itertools
|
||||
import pickle
|
||||
import typing
|
||||
import unittest
|
||||
from annotationlib import (
|
||||
Format,
|
||||
|
@ -15,7 +16,12 @@ from annotationlib import (
|
|||
annotations_to_string,
|
||||
type_repr,
|
||||
)
|
||||
from typing import Unpack
|
||||
from typing import (
|
||||
Unpack,
|
||||
get_type_hints,
|
||||
List,
|
||||
Union,
|
||||
)
|
||||
|
||||
from test import support
|
||||
from test.test_inspect import inspect_stock_annotations
|
||||
|
@ -1205,6 +1211,159 @@ class TestToSource(unittest.TestCase):
|
|||
)
|
||||
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
|
||||
class ForwardRefTests(unittest.TestCase):
|
||||
def test_forwardref_instance_type_error(self):
|
||||
fr = ForwardRef('int')
|
||||
with self.assertRaises(TypeError):
|
||||
isinstance(42, fr)
|
||||
|
||||
def test_forwardref_subclass_type_error(self):
|
||||
fr = ForwardRef('int')
|
||||
with self.assertRaises(TypeError):
|
||||
issubclass(int, fr)
|
||||
|
||||
def test_forwardref_only_str_arg(self):
|
||||
with self.assertRaises(TypeError):
|
||||
ForwardRef(1) # only `str` type is allowed
|
||||
|
||||
def test_forward_equality(self):
|
||||
fr = ForwardRef('int')
|
||||
self.assertEqual(fr, ForwardRef('int'))
|
||||
self.assertNotEqual(List['int'], List[int])
|
||||
self.assertNotEqual(fr, ForwardRef('int', module=__name__))
|
||||
frm = ForwardRef('int', module=__name__)
|
||||
self.assertEqual(frm, ForwardRef('int', module=__name__))
|
||||
self.assertNotEqual(frm, ForwardRef('int', module='__other_name__'))
|
||||
|
||||
def test_forward_equality_get_type_hints(self):
|
||||
c1 = ForwardRef('C')
|
||||
c1_gth = ForwardRef('C')
|
||||
c2 = ForwardRef('C')
|
||||
c2_gth = ForwardRef('C')
|
||||
|
||||
class C:
|
||||
pass
|
||||
def foo(a: c1_gth, b: c2_gth):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()), {'a': C, 'b': C})
|
||||
self.assertEqual(c1, c2)
|
||||
self.assertEqual(c1, c1_gth)
|
||||
self.assertEqual(c1_gth, c2_gth)
|
||||
self.assertEqual(List[c1], List[c1_gth])
|
||||
self.assertNotEqual(List[c1], List[C])
|
||||
self.assertNotEqual(List[c1_gth], List[C])
|
||||
self.assertEqual(Union[c1, c1_gth], Union[c1])
|
||||
self.assertEqual(Union[c1, c1_gth, int], Union[c1, int])
|
||||
|
||||
def test_forward_equality_hash(self):
|
||||
c1 = ForwardRef('int')
|
||||
c1_gth = ForwardRef('int')
|
||||
c2 = ForwardRef('int')
|
||||
c2_gth = ForwardRef('int')
|
||||
|
||||
def foo(a: c1_gth, b: c2_gth):
|
||||
pass
|
||||
get_type_hints(foo, globals(), locals())
|
||||
|
||||
self.assertEqual(hash(c1), hash(c2))
|
||||
self.assertEqual(hash(c1_gth), hash(c2_gth))
|
||||
self.assertEqual(hash(c1), hash(c1_gth))
|
||||
|
||||
c3 = ForwardRef('int', module=__name__)
|
||||
c4 = ForwardRef('int', module='__other_name__')
|
||||
|
||||
self.assertNotEqual(hash(c3), hash(c1))
|
||||
self.assertNotEqual(hash(c3), hash(c1_gth))
|
||||
self.assertNotEqual(hash(c3), hash(c4))
|
||||
self.assertEqual(hash(c3), hash(ForwardRef('int', module=__name__)))
|
||||
|
||||
def test_forward_equality_namespace(self):
|
||||
def namespace1():
|
||||
a = ForwardRef('A')
|
||||
def fun(x: a):
|
||||
pass
|
||||
get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
def namespace2():
|
||||
a = ForwardRef('A')
|
||||
|
||||
class A:
|
||||
pass
|
||||
def fun(x: a):
|
||||
pass
|
||||
|
||||
get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
self.assertEqual(namespace1(), namespace1())
|
||||
self.assertEqual(namespace1(), namespace2())
|
||||
|
||||
def test_forward_repr(self):
|
||||
self.assertEqual(repr(List['int']), "typing.List[ForwardRef('int')]")
|
||||
self.assertEqual(repr(List[ForwardRef('int', module='mod')]),
|
||||
"typing.List[ForwardRef('int', module='mod')]")
|
||||
|
||||
def test_forward_recursion_actually(self):
|
||||
def namespace1():
|
||||
a = ForwardRef('A')
|
||||
A = a
|
||||
def fun(x: a): pass
|
||||
|
||||
ret = get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
def namespace2():
|
||||
a = ForwardRef('A')
|
||||
A = a
|
||||
def fun(x: a): pass
|
||||
|
||||
ret = get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
r1 = namespace1()
|
||||
r2 = namespace2()
|
||||
self.assertIsNot(r1, r2)
|
||||
self.assertEqual(r1, r2)
|
||||
|
||||
def test_syntax_error(self):
|
||||
|
||||
with self.assertRaises(SyntaxError):
|
||||
typing.Generic['/T']
|
||||
|
||||
def test_delayed_syntax_error(self):
|
||||
|
||||
def foo(a: 'Node[T'):
|
||||
pass
|
||||
|
||||
with self.assertRaises(SyntaxError):
|
||||
get_type_hints(foo)
|
||||
|
||||
def test_syntax_error_empty_string(self):
|
||||
for form in [typing.List, typing.Set, typing.Type, typing.Deque]:
|
||||
with self.subTest(form=form):
|
||||
with self.assertRaises(SyntaxError):
|
||||
form['']
|
||||
|
||||
def test_or(self):
|
||||
X = ForwardRef('X')
|
||||
# __or__/__ror__ itself
|
||||
self.assertEqual(X | "x", Union[X, "x"])
|
||||
self.assertEqual("x" | X, Union["x", X])
|
||||
|
||||
def test_multiple_ways_to_create(self):
|
||||
X1 = Union["X"]
|
||||
self.assertIsInstance(X1, ForwardRef)
|
||||
X2 = ForwardRef("X")
|
||||
self.assertIsInstance(X2, ForwardRef)
|
||||
self.assertEqual(X1, X2)
|
||||
|
||||
|
||||
class TestAnnotationLib(unittest.TestCase):
|
||||
def test__all__(self):
|
||||
support.check__all__(self, annotationlib)
|
||||
|
|
|
@ -6074,287 +6074,7 @@ class NoTypeCheck_WithFunction:
|
|||
NoTypeCheck_function = ann_module8.NoTypeCheck_function
|
||||
|
||||
|
||||
class ForwardRefTests(BaseTestCase):
|
||||
|
||||
def test_basics(self):
|
||||
|
||||
class Node(Generic[T]):
|
||||
|
||||
def __init__(self, label: T):
|
||||
self.label = label
|
||||
self.left = self.right = None
|
||||
|
||||
def add_both(self,
|
||||
left: 'Optional[Node[T]]',
|
||||
right: 'Node[T]' = None,
|
||||
stuff: int = None,
|
||||
blah=None):
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def add_left(self, node: Optional['Node[T]']):
|
||||
self.add_both(node, None)
|
||||
|
||||
def add_right(self, node: 'Node[T]' = None):
|
||||
self.add_both(None, node)
|
||||
|
||||
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'], 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'], Node[T])
|
||||
|
||||
def test_forwardref_instance_type_error(self):
|
||||
fr = typing.ForwardRef('int')
|
||||
with self.assertRaises(TypeError):
|
||||
isinstance(42, fr)
|
||||
|
||||
def test_forwardref_subclass_type_error(self):
|
||||
fr = typing.ForwardRef('int')
|
||||
with self.assertRaises(TypeError):
|
||||
issubclass(int, fr)
|
||||
|
||||
def test_forwardref_only_str_arg(self):
|
||||
with self.assertRaises(TypeError):
|
||||
typing.ForwardRef(1) # only `str` type is allowed
|
||||
|
||||
def test_forward_equality(self):
|
||||
fr = typing.ForwardRef('int')
|
||||
self.assertEqual(fr, typing.ForwardRef('int'))
|
||||
self.assertNotEqual(List['int'], List[int])
|
||||
self.assertNotEqual(fr, typing.ForwardRef('int', module=__name__))
|
||||
frm = typing.ForwardRef('int', module=__name__)
|
||||
self.assertEqual(frm, typing.ForwardRef('int', module=__name__))
|
||||
self.assertNotEqual(frm, typing.ForwardRef('int', module='__other_name__'))
|
||||
|
||||
def test_forward_equality_gth(self):
|
||||
c1 = typing.ForwardRef('C')
|
||||
c1_gth = typing.ForwardRef('C')
|
||||
c2 = typing.ForwardRef('C')
|
||||
c2_gth = typing.ForwardRef('C')
|
||||
|
||||
class C:
|
||||
pass
|
||||
def foo(a: c1_gth, b: c2_gth):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()), {'a': C, 'b': C})
|
||||
self.assertEqual(c1, c2)
|
||||
self.assertEqual(c1, c1_gth)
|
||||
self.assertEqual(c1_gth, c2_gth)
|
||||
self.assertEqual(List[c1], List[c1_gth])
|
||||
self.assertNotEqual(List[c1], List[C])
|
||||
self.assertNotEqual(List[c1_gth], List[C])
|
||||
self.assertEqual(Union[c1, c1_gth], Union[c1])
|
||||
self.assertEqual(Union[c1, c1_gth, int], Union[c1, int])
|
||||
|
||||
def test_forward_equality_hash(self):
|
||||
c1 = typing.ForwardRef('int')
|
||||
c1_gth = typing.ForwardRef('int')
|
||||
c2 = typing.ForwardRef('int')
|
||||
c2_gth = typing.ForwardRef('int')
|
||||
|
||||
def foo(a: c1_gth, b: c2_gth):
|
||||
pass
|
||||
get_type_hints(foo, globals(), locals())
|
||||
|
||||
self.assertEqual(hash(c1), hash(c2))
|
||||
self.assertEqual(hash(c1_gth), hash(c2_gth))
|
||||
self.assertEqual(hash(c1), hash(c1_gth))
|
||||
|
||||
c3 = typing.ForwardRef('int', module=__name__)
|
||||
c4 = typing.ForwardRef('int', module='__other_name__')
|
||||
|
||||
self.assertNotEqual(hash(c3), hash(c1))
|
||||
self.assertNotEqual(hash(c3), hash(c1_gth))
|
||||
self.assertNotEqual(hash(c3), hash(c4))
|
||||
self.assertEqual(hash(c3), hash(typing.ForwardRef('int', module=__name__)))
|
||||
|
||||
def test_forward_equality_namespace(self):
|
||||
class A:
|
||||
pass
|
||||
def namespace1():
|
||||
a = typing.ForwardRef('A')
|
||||
def fun(x: a):
|
||||
pass
|
||||
get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
def namespace2():
|
||||
a = typing.ForwardRef('A')
|
||||
|
||||
class A:
|
||||
pass
|
||||
def fun(x: a):
|
||||
pass
|
||||
|
||||
get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
self.assertEqual(namespace1(), namespace1())
|
||||
self.assertEqual(namespace1(), namespace2())
|
||||
|
||||
def test_forward_repr(self):
|
||||
self.assertEqual(repr(List['int']), "typing.List[ForwardRef('int')]")
|
||||
self.assertEqual(repr(List[ForwardRef('int', module='mod')]),
|
||||
"typing.List[ForwardRef('int', module='mod')]")
|
||||
|
||||
def test_union_forward(self):
|
||||
|
||||
def foo(a: Union['T']):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': Union[T]})
|
||||
|
||||
def foo(a: tuple[ForwardRef('T')] | int):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': tuple[T] | int})
|
||||
|
||||
def test_tuple_forward(self):
|
||||
|
||||
def foo(a: Tuple['T']):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': Tuple[T]})
|
||||
|
||||
def foo(a: tuple[ForwardRef('T')]):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': tuple[T]})
|
||||
|
||||
def test_double_forward(self):
|
||||
def foo(a: 'List[\'int\']'):
|
||||
pass
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': List[int]})
|
||||
|
||||
def test_forward_recursion_actually(self):
|
||||
def namespace1():
|
||||
a = typing.ForwardRef('A')
|
||||
A = a
|
||||
def fun(x: a): pass
|
||||
|
||||
ret = get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
def namespace2():
|
||||
a = typing.ForwardRef('A')
|
||||
A = a
|
||||
def fun(x: a): pass
|
||||
|
||||
ret = get_type_hints(fun, globals(), locals())
|
||||
return a
|
||||
|
||||
r1 = namespace1()
|
||||
r2 = namespace2()
|
||||
self.assertIsNot(r1, r2)
|
||||
self.assertEqual(r1, r2)
|
||||
|
||||
def test_union_forward_recursion(self):
|
||||
ValueList = List['Value']
|
||||
Value = Union[str, ValueList]
|
||||
|
||||
class C:
|
||||
foo: List[Value]
|
||||
class D:
|
||||
foo: Union[Value, ValueList]
|
||||
class E:
|
||||
foo: Union[List[Value], ValueList]
|
||||
class F:
|
||||
foo: Union[Value, List[Value], ValueList]
|
||||
|
||||
self.assertEqual(get_type_hints(C, globals(), locals()), get_type_hints(C, globals(), locals()))
|
||||
self.assertEqual(get_type_hints(C, globals(), locals()),
|
||||
{'foo': List[Union[str, List[Union[str, List['Value']]]]]})
|
||||
self.assertEqual(get_type_hints(D, globals(), locals()),
|
||||
{'foo': Union[str, List[Union[str, List['Value']]]]})
|
||||
self.assertEqual(get_type_hints(E, globals(), locals()),
|
||||
{'foo': Union[
|
||||
List[Union[str, List[Union[str, List['Value']]]]],
|
||||
List[Union[str, List['Value']]]
|
||||
]
|
||||
})
|
||||
self.assertEqual(get_type_hints(F, globals(), locals()),
|
||||
{'foo': Union[
|
||||
str,
|
||||
List[Union[str, List['Value']]],
|
||||
List[Union[str, List[Union[str, List['Value']]]]]
|
||||
]
|
||||
})
|
||||
|
||||
def test_callable_forward(self):
|
||||
|
||||
def foo(a: Callable[['T'], 'T']):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': Callable[[T], T]})
|
||||
|
||||
def test_callable_with_ellipsis_forward(self):
|
||||
|
||||
def foo(a: 'Callable[..., T]'):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': Callable[..., T]})
|
||||
|
||||
def test_special_forms_forward(self):
|
||||
|
||||
class C:
|
||||
a: Annotated['ClassVar[int]', (3, 5)] = 4
|
||||
b: Annotated['Final[int]', "const"] = 4
|
||||
x: 'ClassVar' = 4
|
||||
y: 'Final' = 4
|
||||
|
||||
class CF:
|
||||
b: List['Final[int]'] = 4
|
||||
|
||||
self.assertEqual(get_type_hints(C, globals())['a'], ClassVar[int])
|
||||
self.assertEqual(get_type_hints(C, globals())['b'], Final[int])
|
||||
self.assertEqual(get_type_hints(C, globals())['x'], ClassVar)
|
||||
self.assertEqual(get_type_hints(C, globals())['y'], Final)
|
||||
with self.assertRaises(TypeError):
|
||||
get_type_hints(CF, globals()),
|
||||
|
||||
def test_syntax_error(self):
|
||||
|
||||
with self.assertRaises(SyntaxError):
|
||||
Generic['/T']
|
||||
|
||||
def test_delayed_syntax_error(self):
|
||||
|
||||
def foo(a: 'Node[T'):
|
||||
pass
|
||||
|
||||
with self.assertRaises(SyntaxError):
|
||||
get_type_hints(foo)
|
||||
|
||||
def test_syntax_error_empty_string(self):
|
||||
for form in [typing.List, typing.Set, typing.Type, typing.Deque]:
|
||||
with self.subTest(form=form):
|
||||
with self.assertRaises(SyntaxError):
|
||||
form['']
|
||||
|
||||
def test_name_error(self):
|
||||
|
||||
def foo(a: 'Noode[T]'):
|
||||
pass
|
||||
|
||||
with self.assertRaises(NameError):
|
||||
get_type_hints(foo, locals())
|
||||
|
||||
class NoTypeCheckTests(BaseTestCase):
|
||||
def test_no_type_check(self):
|
||||
|
||||
@no_type_check
|
||||
|
@ -6517,35 +6237,6 @@ class ForwardRefTests(BaseTestCase):
|
|||
ith = get_type_hints(C().foo)
|
||||
self.assertEqual(ith, {})
|
||||
|
||||
def test_default_globals(self):
|
||||
code = ("class C:\n"
|
||||
" def foo(self, a: 'C') -> 'D': pass\n"
|
||||
"class D:\n"
|
||||
" def bar(self, b: 'D') -> C: pass\n"
|
||||
)
|
||||
ns = {}
|
||||
exec(code, ns)
|
||||
hints = get_type_hints(ns['C'].foo)
|
||||
self.assertEqual(hints, {'a': ns['C'], 'return': ns['D']})
|
||||
|
||||
def test_final_forward_ref(self):
|
||||
self.assertEqual(gth(Loop, globals())['attr'], Final[Loop])
|
||||
self.assertNotEqual(gth(Loop, globals())['attr'], Final[int])
|
||||
self.assertNotEqual(gth(Loop, globals())['attr'], Final)
|
||||
|
||||
def test_or(self):
|
||||
X = ForwardRef('X')
|
||||
# __or__/__ror__ itself
|
||||
self.assertEqual(X | "x", Union[X, "x"])
|
||||
self.assertEqual("x" | X, Union["x", X])
|
||||
|
||||
def test_multiple_ways_to_create(self):
|
||||
X1 = Union["X"]
|
||||
self.assertIsInstance(X1, ForwardRef)
|
||||
X2 = ForwardRef("X")
|
||||
self.assertIsInstance(X2, ForwardRef)
|
||||
self.assertEqual(X1, X2)
|
||||
|
||||
|
||||
class InternalsTests(BaseTestCase):
|
||||
def test_deprecation_for_no_type_params_passed_to__evaluate(self):
|
||||
|
@ -6844,7 +6535,7 @@ class ForRefExample:
|
|||
pass
|
||||
|
||||
|
||||
class GetTypeHintTests(BaseTestCase):
|
||||
class GetTypeHintsTests(BaseTestCase):
|
||||
def test_get_type_hints_from_various_objects(self):
|
||||
# For invalid objects should fail with TypeError (not AttributeError etc).
|
||||
with self.assertRaises(TypeError):
|
||||
|
@ -7197,6 +6888,158 @@ class GetTypeHintTests(BaseTestCase):
|
|||
self.assertEqual(get_type_hints(func, format=annotationlib.Format.STRING),
|
||||
{'x': 'undefined', 'return': 'undefined'})
|
||||
|
||||
def test_callable_with_ellipsis_forward(self):
|
||||
|
||||
def foo(a: 'Callable[..., T]'):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': Callable[..., T]})
|
||||
|
||||
def test_special_forms_forward(self):
|
||||
|
||||
class C:
|
||||
a: Annotated['ClassVar[int]', (3, 5)] = 4
|
||||
b: Annotated['Final[int]', "const"] = 4
|
||||
x: 'ClassVar' = 4
|
||||
y: 'Final' = 4
|
||||
|
||||
class CF:
|
||||
b: List['Final[int]'] = 4
|
||||
|
||||
self.assertEqual(get_type_hints(C, globals())['a'], ClassVar[int])
|
||||
self.assertEqual(get_type_hints(C, globals())['b'], Final[int])
|
||||
self.assertEqual(get_type_hints(C, globals())['x'], ClassVar)
|
||||
self.assertEqual(get_type_hints(C, globals())['y'], Final)
|
||||
with self.assertRaises(TypeError):
|
||||
get_type_hints(CF, globals()),
|
||||
|
||||
def test_union_forward_recursion(self):
|
||||
ValueList = List['Value']
|
||||
Value = Union[str, ValueList]
|
||||
|
||||
class C:
|
||||
foo: List[Value]
|
||||
class D:
|
||||
foo: Union[Value, ValueList]
|
||||
class E:
|
||||
foo: Union[List[Value], ValueList]
|
||||
class F:
|
||||
foo: Union[Value, List[Value], ValueList]
|
||||
|
||||
self.assertEqual(get_type_hints(C, globals(), locals()), get_type_hints(C, globals(), locals()))
|
||||
self.assertEqual(get_type_hints(C, globals(), locals()),
|
||||
{'foo': List[Union[str, List[Union[str, List['Value']]]]]})
|
||||
self.assertEqual(get_type_hints(D, globals(), locals()),
|
||||
{'foo': Union[str, List[Union[str, List['Value']]]]})
|
||||
self.assertEqual(get_type_hints(E, globals(), locals()),
|
||||
{'foo': Union[
|
||||
List[Union[str, List[Union[str, List['Value']]]]],
|
||||
List[Union[str, List['Value']]]
|
||||
]
|
||||
})
|
||||
self.assertEqual(get_type_hints(F, globals(), locals()),
|
||||
{'foo': Union[
|
||||
str,
|
||||
List[Union[str, List['Value']]],
|
||||
List[Union[str, List[Union[str, List['Value']]]]]
|
||||
]
|
||||
})
|
||||
|
||||
def test_tuple_forward(self):
|
||||
|
||||
def foo(a: Tuple['T']):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': Tuple[T]})
|
||||
|
||||
def foo(a: tuple[ForwardRef('T')]):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': tuple[T]})
|
||||
|
||||
def test_double_forward(self):
|
||||
def foo(a: 'List[\'int\']'):
|
||||
pass
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': List[int]})
|
||||
|
||||
def test_union_forward(self):
|
||||
|
||||
def foo(a: Union['T']):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': Union[T]})
|
||||
|
||||
def foo(a: tuple[ForwardRef('T')] | int):
|
||||
pass
|
||||
|
||||
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
||||
{'a': tuple[T] | int})
|
||||
|
||||
def test_default_globals(self):
|
||||
code = ("class C:\n"
|
||||
" def foo(self, a: 'C') -> 'D': pass\n"
|
||||
"class D:\n"
|
||||
" def bar(self, b: 'D') -> C: pass\n"
|
||||
)
|
||||
ns = {}
|
||||
exec(code, ns)
|
||||
hints = get_type_hints(ns['C'].foo)
|
||||
self.assertEqual(hints, {'a': ns['C'], 'return': ns['D']})
|
||||
|
||||
def test_final_forward_ref(self):
|
||||
gth = get_type_hints
|
||||
self.assertEqual(gth(Loop, globals())['attr'], Final[Loop])
|
||||
self.assertNotEqual(gth(Loop, globals())['attr'], Final[int])
|
||||
self.assertNotEqual(gth(Loop, globals())['attr'], Final)
|
||||
|
||||
def test_name_error(self):
|
||||
|
||||
def foo(a: 'Noode[T]'):
|
||||
pass
|
||||
|
||||
with self.assertRaises(NameError):
|
||||
get_type_hints(foo, locals())
|
||||
|
||||
def test_basics(self):
|
||||
|
||||
class Node(Generic[T]):
|
||||
|
||||
def __init__(self, label: T):
|
||||
self.label = label
|
||||
self.left = self.right = None
|
||||
|
||||
def add_both(self,
|
||||
left: 'Optional[Node[T]]',
|
||||
right: 'Node[T]' = None,
|
||||
stuff: int = None,
|
||||
blah=None):
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def add_left(self, node: Optional['Node[T]']):
|
||||
self.add_both(node, None)
|
||||
|
||||
def add_right(self, node: 'Node[T]' = None):
|
||||
self.add_both(None, node)
|
||||
|
||||
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'], 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'], Node[T])
|
||||
|
||||
|
||||
class GetUtilitiesTestCase(TestCase):
|
||||
def test_get_origin(self):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue