• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""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 *
9from concurrent.futures import Future
10from concurrent.futures.thread import _WorkItem
11from contextlib import AbstractContextManager, AbstractAsyncContextManager
12from contextvars import ContextVar, Token
13from dataclasses import Field
14from functools import partial, partialmethod, cached_property
15from mailbox import Mailbox, _PartialFile
16try:
17    import ctypes
18except ImportError:
19    ctypes = None
20from difflib import SequenceMatcher
21from filecmp import dircmp
22from fileinput import FileInput
23from itertools import chain
24from http.cookies import Morsel
25from multiprocessing.managers import ValueProxy
26from multiprocessing.pool import ApplyResult
27try:
28    from multiprocessing.shared_memory import ShareableList
29except ImportError:
30    # multiprocessing.shared_memory is not available on e.g. Android
31    ShareableList = None
32from multiprocessing.queues import SimpleQueue as MPSimpleQueue
33from os import DirEntry
34from re import Pattern, Match
35from types import GenericAlias, MappingProxyType, AsyncGeneratorType
36from tempfile import TemporaryDirectory, SpooledTemporaryFile
37from urllib.parse import SplitResult, ParseResult
38from unittest.case import _AssertRaisesContext
39from queue import Queue, SimpleQueue
40from weakref import WeakSet, ReferenceType, ref
41import typing
42
43from typing import TypeVar
44T = TypeVar('T')
45K = TypeVar('K')
46V = TypeVar('V')
47
48class BaseTest(unittest.TestCase):
49    """Test basics."""
50    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,
65                     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))
86
87    def test_subscriptable(self):
88        for t in self.generic_types:
89            if t is None:
90                continue
91            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):
174        from typing import List, Dict, Callable
175        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,))
196        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))
211
212    def test_parameter_chaining(self):
213        from typing import List, Dict, Union, Callable
214        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])
217        self.assertEqual(dict[K, V][str, int], dict[str, int])
218        self.assertEqual(dict[T, T][int], dict[int, int])
219
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
233        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):
285        a = typing.Union[list[T], tuple[T, ...]]
286        self.assertEqual(a.__args__, (list[T], tuple[T, ...]))
287        self.assertEqual(a.__parameters__, (T,))
288
289    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)
294
295    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
304    def test_no_kwargs(self):
305        # bpo-42576
306        with self.assertRaises(TypeError):
307            GenericAlias(bad=float)
308
309    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
350            # 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
356        with self.subTest("Testing type erasure"):
357            class C1(Callable):
358                def __call__(self):
359                    return None
360            a = C1[[int], T]
361            self.assertIs(a().__class__, C1)
362            self.assertEqual(a().__orig_class__, C1[[int], T])
363
364        # bpo-42195
365        with self.subTest("Testing collections.abc.Callable's consistency "
366                          "with typing.Callable"):
367            c1 = typing.Callable[[int, str], dict]
368            c2 = Callable[[int, str], dict]
369            self.assertEqual(c1.__args__, c2.__args__)
370            self.assertEqual(hash(c1.__args__), hash(c2.__args__))
371
372
373if __name__ == "__main__":
374    unittest.main()
375