blob: 9f927392fc874004cf8d13c937fcc595f2d77b78 [file] [log] [blame]
Guido van Rossum48b069a2020-04-07 09:50:06 -07001"""Tests for C-implemented GenericAlias."""
2
3import unittest
4import pickle
5from collections import (
6 defaultdict, deque, OrderedDict, Counter, UserDict, UserList
7)
8from collections.abc import *
Batuhan Taşkaya03615562020-04-10 17:46:36 +03009from concurrent.futures import Future
10from concurrent.futures.thread import _WorkItem
Guido van Rossum48b069a2020-04-07 09:50:06 -070011from contextlib import AbstractContextManager, AbstractAsyncContextManager
Ethan Smithd01628e2020-04-14 16:14:15 -070012from contextvars import ContextVar, Token
13from dataclasses import Field
14from functools import partial, partialmethod, cached_property
15from mailbox import Mailbox, _PartialFile
Victor Stinnerf44693e2020-08-07 17:56:42 +020016try:
17 import ctypes
18except ImportError:
19 ctypes = None
Ethan Smithe3ec44d2020-04-09 21:47:31 -070020from difflib import SequenceMatcher
21from filecmp import dircmp
22from fileinput import FileInput
Ethan Smitha8403d02020-04-09 20:28:08 -070023from itertools import chain
Batuhan Taşkaya03615562020-04-10 17:46:36 +030024from http.cookies import Morsel
25from multiprocessing.managers import ValueProxy
26from multiprocessing.pool import ApplyResult
Chih-Hsuan Yen25a68332020-04-14 06:00:16 +080027try:
28 from multiprocessing.shared_memory import ShareableList
29except ImportError:
30 # multiprocessing.shared_memory is not available on e.g. Android
31 ShareableList = None
Saiyang Goub2c0a432020-10-09 13:00:15 -070032from multiprocessing.queues import SimpleQueue as MPSimpleQueue
Batuhan Taşkayaf9dd51e2020-04-08 00:37:19 +030033from os import DirEntry
Guido van Rossum48b069a2020-04-07 09:50:06 -070034from re import Pattern, Match
Ethan Smith7c4185d2020-04-09 21:25:53 -070035from types import GenericAlias, MappingProxyType, AsyncGeneratorType
Batuhan Taşkaya03615562020-04-10 17:46:36 +030036from tempfile import TemporaryDirectory, SpooledTemporaryFile
37from urllib.parse import SplitResult, ParseResult
38from unittest.case import _AssertRaisesContext
39from queue import Queue, SimpleQueue
Ethan Smith8ef87502020-04-13 21:54:40 -070040from weakref import WeakSet, ReferenceType, ref
Guido van Rossum48b069a2020-04-07 09:50:06 -070041import typing
42
43from typing import TypeVar
44T = TypeVar('T')
Serhiy Storchaka41a64582020-05-04 10:56:05 +030045K = TypeVar('K')
46V = TypeVar('V')
Guido van Rossum48b069a2020-04-07 09:50:06 -070047
48class BaseTest(unittest.TestCase):
49 """Test basics."""
kj384b7a42020-11-16 11:27:23 +080050 generic_types = [type, tuple, list, dict, set, frozenset, enumerate,
51 defaultdict, deque,
52 SequenceMatcher,
53 dircmp,
54 FileInput,
55 OrderedDict, Counter, UserDict, UserList,
56 Pattern, Match,
57 partial, partialmethod, cached_property,
58 AbstractContextManager, AbstractAsyncContextManager,
59 Awaitable, Coroutine,
60 AsyncIterable, AsyncIterator,
61 AsyncGenerator, Generator,
62 Iterable, Iterator,
63 Reversible,
64 Container, Collection,
kj384b7a42020-11-16 11:27:23 +080065 Mailbox, _PartialFile,
66 ContextVar, Token,
67 Field,
68 Set, MutableSet,
69 Mapping, MutableMapping, MappingView,
70 KeysView, ItemsView, ValuesView,
71 Sequence, MutableSequence,
72 MappingProxyType, AsyncGeneratorType,
73 DirEntry,
74 chain,
75 TemporaryDirectory, SpooledTemporaryFile,
76 Queue, SimpleQueue,
77 _AssertRaisesContext,
78 SplitResult, ParseResult,
79 ValueProxy, ApplyResult,
80 WeakSet, ReferenceType, ref,
81 ShareableList, MPSimpleQueue,
82 Future, _WorkItem,
83 Morsel]
84 if ctypes is not None:
85 generic_types.extend((ctypes.Array, ctypes.LibraryLoader))
Guido van Rossum48b069a2020-04-07 09:50:06 -070086
87 def test_subscriptable(self):
kj384b7a42020-11-16 11:27:23 +080088 for t in self.generic_types:
Chih-Hsuan Yen25a68332020-04-14 06:00:16 +080089 if t is None:
90 continue
Guido van Rossum48b069a2020-04-07 09:50:06 -070091 tname = t.__name__
92 with self.subTest(f"Testing {tname}"):
93 alias = t[int]
94 self.assertIs(alias.__origin__, t)
95 self.assertEqual(alias.__args__, (int,))
96 self.assertEqual(alias.__parameters__, ())
97
98 def test_unsubscriptable(self):
99 for t in int, str, float, Sized, Hashable:
100 tname = t.__name__
101 with self.subTest(f"Testing {tname}"):
102 with self.assertRaises(TypeError):
103 t[int]
104
105 def test_instantiate(self):
106 for t in tuple, list, dict, set, frozenset, defaultdict, deque:
107 tname = t.__name__
108 with self.subTest(f"Testing {tname}"):
109 alias = t[int]
110 self.assertEqual(alias(), t())
111 if t is dict:
112 self.assertEqual(alias(iter([('a', 1), ('b', 2)])), dict(a=1, b=2))
113 self.assertEqual(alias(a=1, b=2), dict(a=1, b=2))
114 elif t is defaultdict:
115 def default():
116 return 'value'
117 a = alias(default)
118 d = defaultdict(default)
119 self.assertEqual(a['test'], d['test'])
120 else:
121 self.assertEqual(alias(iter((1, 2, 3))), t((1, 2, 3)))
122
123 def test_unbound_methods(self):
124 t = list[int]
125 a = t()
126 t.append(a, 'foo')
127 self.assertEqual(a, ['foo'])
128 x = t.__getitem__(a, 0)
129 self.assertEqual(x, 'foo')
130 self.assertEqual(t.__len__(a), 1)
131
132 def test_subclassing(self):
133 class C(list[int]):
134 pass
135 self.assertEqual(C.__bases__, (list,))
136 self.assertEqual(C.__class__, type)
137
138 def test_class_methods(self):
139 t = dict[int, None]
140 self.assertEqual(dict.fromkeys(range(2)), {0: None, 1: None}) # This works
141 self.assertEqual(t.fromkeys(range(2)), {0: None, 1: None}) # Should be equivalent
142
143 def test_no_chaining(self):
144 t = list[int]
145 with self.assertRaises(TypeError):
146 t[int]
147
148 def test_generic_subclass(self):
149 class MyList(list):
150 pass
151 t = MyList[int]
152 self.assertIs(t.__origin__, MyList)
153 self.assertEqual(t.__args__, (int,))
154 self.assertEqual(t.__parameters__, ())
155
156 def test_repr(self):
157 class MyList(list):
158 pass
159 self.assertEqual(repr(list[str]), 'list[str]')
160 self.assertEqual(repr(list[()]), 'list[()]')
161 self.assertEqual(repr(tuple[int, ...]), 'tuple[int, ...]')
162 self.assertTrue(repr(MyList[int]).endswith('.BaseTest.test_repr.<locals>.MyList[int]'))
163 self.assertEqual(repr(list[str]()), '[]') # instances should keep their normal repr
164
165 def test_exposed_type(self):
166 import types
167 a = types.GenericAlias(list, int)
168 self.assertEqual(str(a), 'list[int]')
169 self.assertIs(a.__origin__, list)
170 self.assertEqual(a.__args__, (int,))
171 self.assertEqual(a.__parameters__, ())
172
173 def test_parameters(self):
Serhiy Storchaka41a64582020-05-04 10:56:05 +0300174 from typing import List, Dict, Callable
Guido van Rossum48b069a2020-04-07 09:50:06 -0700175 D0 = dict[str, int]
176 self.assertEqual(D0.__args__, (str, int))
177 self.assertEqual(D0.__parameters__, ())
178 D1a = dict[str, V]
179 self.assertEqual(D1a.__args__, (str, V))
180 self.assertEqual(D1a.__parameters__, (V,))
181 D1b = dict[K, int]
182 self.assertEqual(D1b.__args__, (K, int))
183 self.assertEqual(D1b.__parameters__, (K,))
184 D2a = dict[K, V]
185 self.assertEqual(D2a.__args__, (K, V))
186 self.assertEqual(D2a.__parameters__, (K, V))
187 D2b = dict[T, T]
188 self.assertEqual(D2b.__args__, (T, T))
189 self.assertEqual(D2b.__parameters__, (T,))
190 L0 = list[str]
191 self.assertEqual(L0.__args__, (str,))
192 self.assertEqual(L0.__parameters__, ())
193 L1 = list[T]
194 self.assertEqual(L1.__args__, (T,))
195 self.assertEqual(L1.__parameters__, (T,))
Serhiy Storchaka41a64582020-05-04 10:56:05 +0300196 L2 = list[list[T]]
197 self.assertEqual(L2.__args__, (list[T],))
198 self.assertEqual(L2.__parameters__, (T,))
199 L3 = list[List[T]]
200 self.assertEqual(L3.__args__, (List[T],))
201 self.assertEqual(L3.__parameters__, (T,))
202 L4a = list[Dict[K, V]]
203 self.assertEqual(L4a.__args__, (Dict[K, V],))
204 self.assertEqual(L4a.__parameters__, (K, V))
205 L4b = list[Dict[T, int]]
206 self.assertEqual(L4b.__args__, (Dict[T, int],))
207 self.assertEqual(L4b.__parameters__, (T,))
208 L5 = list[Callable[[K, V], K]]
209 self.assertEqual(L5.__args__, (Callable[[K, V], K],))
210 self.assertEqual(L5.__parameters__, (K, V))
Guido van Rossum48b069a2020-04-07 09:50:06 -0700211
212 def test_parameter_chaining(self):
Serhiy Storchaka41a64582020-05-04 10:56:05 +0300213 from typing import List, Dict, Union, Callable
Guido van Rossum48b069a2020-04-07 09:50:06 -0700214 self.assertEqual(list[T][int], list[int])
215 self.assertEqual(dict[str, T][int], dict[str, int])
216 self.assertEqual(dict[T, int][str], dict[str, int])
Serhiy Storchaka41a64582020-05-04 10:56:05 +0300217 self.assertEqual(dict[K, V][str, int], dict[str, int])
Guido van Rossum48b069a2020-04-07 09:50:06 -0700218 self.assertEqual(dict[T, T][int], dict[int, int])
Serhiy Storchaka41a64582020-05-04 10:56:05 +0300219
220 self.assertEqual(list[list[T]][int], list[list[int]])
221 self.assertEqual(list[dict[T, int]][str], list[dict[str, int]])
222 self.assertEqual(list[dict[str, T]][int], list[dict[str, int]])
223 self.assertEqual(list[dict[K, V]][str, int], list[dict[str, int]])
224 self.assertEqual(dict[T, list[int]][str], dict[str, list[int]])
225
226 self.assertEqual(list[List[T]][int], list[List[int]])
227 self.assertEqual(list[Dict[K, V]][str, int], list[Dict[str, int]])
228 self.assertEqual(list[Union[K, V]][str, int], list[Union[str, int]])
229 self.assertEqual(list[Callable[[K, V], K]][str, int],
230 list[Callable[[str, int], str]])
231 self.assertEqual(dict[T, List[int]][str], dict[str, List[int]])
232
Guido van Rossum48b069a2020-04-07 09:50:06 -0700233 with self.assertRaises(TypeError):
234 list[int][int]
235 dict[T, int][str, int]
236 dict[str, T][str, int]
237 dict[T, T][str, int]
238
239 def test_equality(self):
240 self.assertEqual(list[int], list[int])
241 self.assertEqual(dict[str, int], dict[str, int])
242 self.assertNotEqual(dict[str, int], dict[str, str])
243 self.assertNotEqual(list, list[int])
244 self.assertNotEqual(list[int], list)
245
246 def test_isinstance(self):
247 self.assertTrue(isinstance([], list))
248 with self.assertRaises(TypeError):
249 isinstance([], list[str])
250
251 def test_issubclass(self):
252 class L(list): ...
253 self.assertTrue(issubclass(L, list))
254 with self.assertRaises(TypeError):
255 issubclass(L, list[str])
256
257 def test_type_generic(self):
258 t = type[int]
259 Test = t('Test', (), {})
260 self.assertTrue(isinstance(Test, type))
261 test = Test()
262 self.assertEqual(t(test), Test)
263 self.assertEqual(t(0), int)
264
265 def test_type_subclass_generic(self):
266 class MyType(type):
267 pass
268 with self.assertRaises(TypeError):
269 MyType[int]
270
271 def test_pickle(self):
272 alias = GenericAlias(list, T)
273 s = pickle.dumps(alias)
274 loaded = pickle.loads(s)
275 self.assertEqual(alias.__origin__, loaded.__origin__)
276 self.assertEqual(alias.__args__, loaded.__args__)
277 self.assertEqual(alias.__parameters__, loaded.__parameters__)
278
279 def test_union(self):
280 a = typing.Union[list[int], list[str]]
281 self.assertEqual(a.__args__, (list[int], list[str]))
282 self.assertEqual(a.__parameters__, ())
283
284 def test_union_generic(self):
Guido van Rossum48b069a2020-04-07 09:50:06 -0700285 a = typing.Union[list[T], tuple[T, ...]]
286 self.assertEqual(a.__args__, (list[T], tuple[T, ...]))
287 self.assertEqual(a.__parameters__, (T,))
288
Batuhan Taskaya2e877742020-09-16 00:58:32 +0300289 def test_dir(self):
290 dir_of_gen_alias = set(dir(list[int]))
291 self.assertTrue(dir_of_gen_alias.issuperset(dir(list)))
292 for generic_alias_property in ("__origin__", "__args__", "__parameters__"):
293 self.assertIn(generic_alias_property, dir_of_gen_alias)
Guido van Rossum48b069a2020-04-07 09:50:06 -0700294
kj384b7a42020-11-16 11:27:23 +0800295 def test_weakref(self):
296 for t in self.generic_types:
297 if t is None:
298 continue
299 tname = t.__name__
300 with self.subTest(f"Testing {tname}"):
301 alias = t[int]
302 self.assertEqual(ref(alias)(), alias)
303
kj804d6892020-12-05 23:02:14 +0700304 def test_no_kwargs(self):
305 # bpo-42576
306 with self.assertRaises(TypeError):
307 GenericAlias(bad=float)
308
kj463c7d32020-12-14 02:38:24 +0800309 def test_subclassing_types_genericalias(self):
310 class SubClass(GenericAlias): ...
311 alias = SubClass(list, int)
312 class Bad(GenericAlias):
313 def __new__(cls, *args, **kwargs):
314 super().__new__(cls, *args, **kwargs)
315
316 self.assertEqual(alias, list[int])
317 with self.assertRaises(TypeError):
318 Bad(list, int, bad=int)
319
320 def test_abc_callable(self):
321 # A separate test is needed for Callable since it uses a subclass of
322 # GenericAlias.
323 alias = Callable[[int, str], float]
324 with self.subTest("Testing subscription"):
325 self.assertIs(alias.__origin__, Callable)
326 self.assertEqual(alias.__args__, (int, str, float))
327 self.assertEqual(alias.__parameters__, ())
328
329 with self.subTest("Testing instance checks"):
330 self.assertIsInstance(alias, GenericAlias)
331
332 with self.subTest("Testing weakref"):
333 self.assertEqual(ref(alias)(), alias)
334
335 with self.subTest("Testing pickling"):
336 s = pickle.dumps(alias)
337 loaded = pickle.loads(s)
338 self.assertEqual(alias.__origin__, loaded.__origin__)
339 self.assertEqual(alias.__args__, loaded.__args__)
340 self.assertEqual(alias.__parameters__, loaded.__parameters__)
341
342 with self.subTest("Testing TypeVar substitution"):
343 C1 = Callable[[int, T], T]
344 C2 = Callable[[K, T], V]
345 C3 = Callable[..., T]
346 self.assertEqual(C1[str], Callable[[int, str], str])
347 self.assertEqual(C2[int, float, str], Callable[[int, float], str])
348 self.assertEqual(C3[int], Callable[..., int])
349
kj6dd3da32020-12-24 10:47:40 +0800350 # multi chaining
351 C4 = C2[int, V, str]
352 self.assertEqual(repr(C4).split(".")[-1], "Callable[[int, ~V], str]")
353 self.assertEqual(repr(C4[dict]).split(".")[-1], "Callable[[int, dict], str]")
354 self.assertEqual(C4[dict], Callable[[int, dict], str])
355
Ken Jin859577c2021-04-28 23:38:14 +0800356 # substitute a nested GenericAlias (both typing and the builtin
357 # version)
358 C5 = Callable[[typing.List[T], tuple[K, T], V], int]
359 self.assertEqual(C5[int, str, float],
360 Callable[[typing.List[int], tuple[str, int], float], int])
361
kj463c7d32020-12-14 02:38:24 +0800362 with self.subTest("Testing type erasure"):
363 class C1(Callable):
364 def __call__(self):
365 return None
366 a = C1[[int], T]
367 self.assertIs(a().__class__, C1)
368 self.assertEqual(a().__orig_class__, C1[[int], T])
369
370 # bpo-42195
371 with self.subTest("Testing collections.abc.Callable's consistency "
372 "with typing.Callable"):
373 c1 = typing.Callable[[int, str], dict]
374 c2 = Callable[[int, str], dict]
375 self.assertEqual(c1.__args__, c2.__args__)
376 self.assertEqual(hash(c1.__args__), hash(c2.__args__))
377
kj73607be2020-12-24 12:33:48 +0800378 with self.subTest("Testing ParamSpec uses"):
379 P = typing.ParamSpec('P')
380 C1 = Callable[P, T]
381 # substitution
382 self.assertEqual(C1[int, str], Callable[[int], str])
383 self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
384 self.assertEqual(repr(C1).split(".")[-1], "Callable[~P, ~T]")
385 self.assertEqual(repr(C1[int, str]).split(".")[-1], "Callable[[int], str]")
386
387 C2 = Callable[P, int]
388 # special case in PEP 612 where
389 # X[int, str, float] == X[[int, str, float]]
390 self.assertEqual(C2[int, str, float], C2[[int, str, float]])
391 self.assertEqual(repr(C2).split(".")[-1], "Callable[~P, int]")
392 self.assertEqual(repr(C2[int, str]).split(".")[-1], "Callable[[int, str], int]")
393
394 with self.subTest("Testing Concatenate uses"):
395 P = typing.ParamSpec('P')
396 C1 = Callable[typing.Concatenate[int, P], int]
397 self.assertEqual(repr(C1), "collections.abc.Callable"
398 "[typing.Concatenate[int, ~P], int]")
kj384b7a42020-11-16 11:27:23 +0800399
Ken Jin859577c2021-04-28 23:38:14 +0800400 with self.subTest("Testing TypeErrors"):
401 with self.assertRaisesRegex(TypeError, "variables left in"):
402 alias[int]
403 P = typing.ParamSpec('P')
404 C1 = Callable[P, T]
405 with self.assertRaisesRegex(TypeError, "many arguments for"):
406 C1[int, str, str]
407 with self.assertRaisesRegex(TypeError, "few arguments for"):
408 C1[int]
409
410
Guido van Rossum48b069a2020-04-07 09:50:06 -0700411if __name__ == "__main__":
412 unittest.main()