Skip to content

Commit cfb31b5

Browse files
committed
Add more tests based on coverage report
1 parent abfdf49 commit cfb31b5

File tree

4 files changed

+116
-8
lines changed

4 files changed

+116
-8
lines changed

python2/test_typing.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,12 @@ def test_repr(self):
251251
self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__)
252252
u = Union[int, Employee]
253253
self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__)
254+
T = TypeVar('T')
255+
u = Union[T, int][int]
256+
self.assertEqual(repr(u), repr(int))
257+
u = Union[List[int], int]
258+
self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]')
259+
254260

255261
def test_cannot_subclass(self):
256262
with self.assertRaises(TypeError):
@@ -299,6 +305,14 @@ def test_union_instance_type_error(self):
299305
with self.assertRaises(TypeError):
300306
isinstance(42, Union[int, str])
301307

308+
def test_no_eval_union(self):
309+
u = Union[int, str]
310+
self.assertIs(u._eval_type({}, {}), u)
311+
312+
def test_function_repr_union(self):
313+
def fun(): pass
314+
self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]')
315+
302316
def test_union_str_pattern(self):
303317
# Shouldn't crash; see http://bugs.python.org/issue25390
304318
A = Union[str, Pattern]
@@ -835,6 +849,8 @@ def test_fail_with_bare_generic(self):
835849
Tuple[Generic[T]]
836850
with self.assertRaises(TypeError):
837851
List[typing._Protocol]
852+
with self.assertRaises(TypeError):
853+
isinstance(1, Generic)
838854

839855
def test_type_erasure_special(self):
840856
T = TypeVar('T')
@@ -1173,6 +1189,19 @@ def test_syntax_error(self):
11731189
with self.assertRaises(SyntaxError):
11741190
Generic['/T']
11751191

1192+
def test_forwardref_subclass_type_error(self):
1193+
fr = typing._ForwardRef('int')
1194+
with self.assertRaises(TypeError):
1195+
issubclass(int, fr)
1196+
1197+
def test_forward_equality(self):
1198+
fr = typing._ForwardRef('int')
1199+
self.assertEqual(fr, typing._ForwardRef('int'))
1200+
self.assertNotEqual(List['int'], List[int])
1201+
1202+
def test_forward_repr(self):
1203+
self.assertEqual(repr(List['int']), "typing.List[_ForwardRef(%r)]" % 'int')
1204+
11761205

11771206
class OverloadTests(BaseTestCase):
11781207

@@ -1638,6 +1667,12 @@ def test_basics(self):
16381667
Pattern[Union[str, bytes]]
16391668
Match[Union[bytes, str]]
16401669

1670+
def test_alias_equality(self):
1671+
self.assertEqual(Pattern[str], Pattern[str])
1672+
self.assertNotEqual(Pattern[str], Pattern[bytes])
1673+
self.assertNotEqual(Pattern[str], Match[str])
1674+
self.assertNotEqual(Pattern[str], str)
1675+
16411676
def test_errors(self):
16421677
with self.assertRaises(TypeError):
16431678
# Doesn't fit AnyStr.
@@ -1652,6 +1687,9 @@ def test_errors(self):
16521687
with self.assertRaises(TypeError):
16531688
# We don't support isinstance().
16541689
isinstance(42, Pattern[str])
1690+
with self.assertRaises(TypeError):
1691+
# We don't support issubclass().
1692+
issubclass(Pattern[bytes], Pattern[str])
16551693

16561694
def test_repr(self):
16571695
self.assertEqual(repr(Pattern), 'Pattern[~AnyStr]')

python2/typing.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,15 @@ def _eval_type(self, globalns, localns):
225225
self.__forward_evaluated__ = True
226226
return self.__forward_value__
227227

228+
def __eq__(self, other):
229+
if not isinstance(other, _ForwardRef):
230+
return NotImplemented
231+
return (self.__forward_arg__ == other.__forward_arg__ and
232+
self.__forward_value__ == other.__forward_value__)
233+
234+
def __hash__(self):
235+
return hash((self.__forward_arg__, self.__forward_value__))
236+
228237
def __instancecheck__(self, obj):
229238
raise TypeError("Forward references cannot be used with isinstance().")
230239

@@ -281,6 +290,14 @@ def __getitem__(self, parameter):
281290
return self.__class__(self.name, parameter,
282291
self.impl_type, self.type_checker)
283292

293+
def __eq__(self, other):
294+
if not isinstance(other, _TypeAlias):
295+
return NotImplemented
296+
return self.name == other.name and self.type_var == other.type_var
297+
298+
def __hash__(self):
299+
return hash((self.name, self.type_var))
300+
284301
def __instancecheck__(self, obj):
285302
if not isinstance(self.type_var, TypeVar):
286303
raise TypeError("Parameterized type aliases cannot be used "
@@ -1064,10 +1081,9 @@ def __new__(cls, name, bases, namespace,
10641081
# with issubclass() and isinstance() in the same way as their
10651082
# collections.abc counterparts (e.g., isinstance([], Iterable)).
10661083
if (
1067-
# allow overriding
10681084
'__subclasshook__' not in namespace and extra or
1069-
hasattr(self.__subclasshook__, '__name__') and
1070-
self.__subclasshook__.__name__ == '__extrahook__'
1085+
# allow overriding
1086+
getattr(self.__subclasshook__, '__name__', '') == '__extrahook__'
10711087
):
10721088
self.__subclasshook__ = _make_subclasshook(self)
10731089

@@ -1245,6 +1261,10 @@ def __new__(cls, *args, **kwds):
12451261
return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
12461262

12471263

1264+
# prevent class and instance checks against plain Generic
1265+
Generic.__subclasshook__ = _make_subclasshook(Generic)
1266+
1267+
12481268
class _TypingEmpty(object):
12491269
"""Internal placeholder for () or []. Used by TupleMeta and CallableMeta
12501270
to allow empty list/tuple in specific places, without allowing them

src/test_typing.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@ def test_repr(self):
254254
self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__)
255255
u = Union[int, Employee]
256256
self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__)
257+
T = TypeVar('T')
258+
u = Union[T, int][int]
259+
self.assertEqual(repr(u), repr(int))
260+
u = Union[List[int], int]
261+
self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]')
257262

258263
def test_cannot_subclass(self):
259264
with self.assertRaises(TypeError):
@@ -304,6 +309,15 @@ def test_union_instance_type_error(self):
304309
with self.assertRaises(TypeError):
305310
isinstance(42, Union[int, str])
306311

312+
def test_no_eval_union(self):
313+
u = Union[int, str]
314+
def f(x: u): ...
315+
self.assertIs(get_type_hints(f)['x'], u)
316+
317+
def test_function_repr_union(self):
318+
def fun() -> int: ...
319+
self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]')
320+
307321
def test_union_str_pattern(self):
308322
# Shouldn't crash; see http://bugs.python.org/issue25390
309323
A = Union[str, Pattern]
@@ -874,6 +888,8 @@ def test_fail_with_bare_generic(self):
874888
Tuple[Generic[T]]
875889
with self.assertRaises(TypeError):
876890
List[typing._Protocol]
891+
with self.assertRaises(TypeError):
892+
isinstance(1, Generic)
877893

878894
def test_type_erasure_special(self):
879895
T = TypeVar('T')
@@ -1242,6 +1258,19 @@ def test_forwardref_instance_type_error(self):
12421258
with self.assertRaises(TypeError):
12431259
isinstance(42, fr)
12441260

1261+
def test_forwardref_subclass_type_error(self):
1262+
fr = typing._ForwardRef('int')
1263+
with self.assertRaises(TypeError):
1264+
issubclass(int, fr)
1265+
1266+
def test_forward_equality(self):
1267+
fr = typing._ForwardRef('int')
1268+
self.assertEqual(fr, typing._ForwardRef('int'))
1269+
self.assertNotEqual(List['int'], List[int])
1270+
1271+
def test_forward_repr(self):
1272+
self.assertEqual(repr(List['int']), "typing.List[_ForwardRef('int')]")
1273+
12451274
def test_union_forward(self):
12461275

12471276
def foo(a: Union['T']):
@@ -1440,11 +1469,16 @@ class A:
14401469
class B(A):
14411470
x: ClassVar[Optional['B']] = None
14421471
y: int
1472+
b: int
14431473
class CSub(B):
14441474
z: ClassVar['CSub'] = B()
14451475
class G(Generic[T]):
14461476
lst: ClassVar[List[T]] = []
14471477
1478+
class NoneAndForward:
1479+
parent: 'NoneAndForward'
1480+
meaning: None
1481+
14481482
class CoolEmployee(NamedTuple):
14491483
name: str
14501484
cool: int
@@ -1465,6 +1499,7 @@ def double(self):
14651499
# fake names for the sake of static analysis
14661500
ann_module = ann_module2 = ann_module3 = None
14671501
A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = XMeth = object
1502+
NoneAndForward = object
14681503

14691504
gth = get_type_hints
14701505

@@ -1499,6 +1534,8 @@ def test_get_type_hints_classes(self):
14991534
{'y': Optional[ann_module.C]})
15001535
self.assertEqual(gth(ann_module.S), {'x': str, 'y': str})
15011536
self.assertEqual(gth(ann_module.foo), {'x': int})
1537+
self.assertEqual(gth(NoneAndForward, globals()),
1538+
{'parent': NoneAndForward, 'meaning': type(None)})
15021539

15031540
@skipUnless(PY36, 'Python 3.6 required')
15041541
def test_respect_no_type_check(self):
@@ -1539,9 +1576,10 @@ def test_get_type_hints_ClassVar(self):
15391576
self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__),
15401577
{'var': typing.ClassVar[ann_module2.CV]})
15411578
self.assertEqual(gth(B, globals()),
1542-
{'y': int, 'x': ClassVar[Optional[B]]})
1579+
{'y': int, 'x': ClassVar[Optional[B]], 'b': int})
15431580
self.assertEqual(gth(CSub, globals()),
1544-
{'z': ClassVar[CSub], 'y': int, 'x': ClassVar[Optional[B]]})
1581+
{'z': ClassVar[CSub], 'y': int, 'b': int,
1582+
'x': ClassVar[Optional[B]]})
15451583
self.assertEqual(gth(G), {'lst': ClassVar[List[T]]})
15461584

15471585

@@ -2177,6 +2215,12 @@ def test_basics(self):
21772215
Pattern[Union[str, bytes]]
21782216
Match[Union[bytes, str]]
21792217

2218+
def test_alias_equality(self):
2219+
self.assertEqual(Pattern[str], Pattern[str])
2220+
self.assertNotEqual(Pattern[str], Pattern[bytes])
2221+
self.assertNotEqual(Pattern[str], Match[str])
2222+
self.assertNotEqual(Pattern[str], str)
2223+
21802224
def test_errors(self):
21812225
with self.assertRaises(TypeError):
21822226
# Doesn't fit AnyStr.
@@ -2191,6 +2235,9 @@ def test_errors(self):
21912235
with self.assertRaises(TypeError):
21922236
# We don't support isinstance().
21932237
isinstance(42, Pattern[str])
2238+
with self.assertRaises(TypeError):
2239+
# We don't support issubclass().
2240+
issubclass(Pattern[bytes], Pattern[str])
21942241

21952242
def test_repr(self):
21962243
self.assertEqual(repr(Pattern), 'Pattern[~AnyStr]')

src/typing.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -994,10 +994,9 @@ def __new__(cls, name, bases, namespace,
994994
# with issubclass() and isinstance() in the same way as their
995995
# collections.abc counterparts (e.g., isinstance([], Iterable)).
996996
if (
997-
# allow overriding
998997
'__subclasshook__' not in namespace and extra or
999-
hasattr(self.__subclasshook__, '__name__') and
1000-
self.__subclasshook__.__name__ == '__extrahook__'
998+
# allow overriding
999+
getattr(self.__subclasshook__, '__name__', '') == '__extrahook__'
10011000
):
10021001
self.__subclasshook__ = _make_subclasshook(self)
10031002
if isinstance(extra, abc.ABCMeta):
@@ -1170,6 +1169,10 @@ def __new__(cls, *args, **kwds):
11701169
return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
11711170

11721171

1172+
# prevent class and instance checks against plain Generic
1173+
Generic.__subclasshook__ = _make_subclasshook(Generic)
1174+
1175+
11731176
class _TypingEmpty:
11741177
"""Internal placeholder for () or []. Used by TupleMeta and CallableMeta
11751178
to allow empty list/tuple in specific places, without allowing them

0 commit comments

Comments
 (0)