• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Tests for C-implemented GenericAlias."""
2
3import unittest
4import pickle
5import copy
6from collections import (
7    defaultdict, deque, OrderedDict, Counter, UserDict, UserList
8)
9from collections.abc import *
10from concurrent.futures import Future
11from concurrent.futures.thread import _WorkItem
12from contextlib import AbstractContextManager, AbstractAsyncContextManager
13from contextvars import ContextVar, Token
14from dataclasses import Field
15from functools import partial, partialmethod, cached_property
16from mailbox import Mailbox, _PartialFile
17try:
18    import ctypes
19except ImportError:
20    ctypes = None
21from difflib import SequenceMatcher
22from filecmp import dircmp
23from fileinput import FileInput
24from itertools import chain
25from http.cookies import Morsel
26from multiprocessing.managers import ValueProxy
27from multiprocessing.pool import ApplyResult
28try:
29    from multiprocessing.shared_memory import ShareableList
30except ImportError:
31    # multiprocessing.shared_memory is not available on e.g. Android
32    ShareableList = None
33from multiprocessing.queues import SimpleQueue as MPSimpleQueue
34from os import DirEntry
35from re import Pattern, Match
36from types import GenericAlias, MappingProxyType, AsyncGeneratorType
37from tempfile import TemporaryDirectory, SpooledTemporaryFile
38from urllib.parse import SplitResult, ParseResult
39from unittest.case import _AssertRaisesContext
40from queue import Queue, SimpleQueue
41from weakref import WeakSet, ReferenceType, ref
42import typing
43
44from typing import TypeVar
45T = TypeVar('T')
46K = TypeVar('K')
47V = TypeVar('V')
48
49class BaseTest(unittest.TestCase):
50    """Test basics."""
51    generic_types = [type, tuple, list, dict, set, frozenset, enumerate,
52                     defaultdict, deque,
53                     SequenceMatcher,
54                     dircmp,
55                     FileInput,
56                     OrderedDict, Counter, UserDict, UserList,
57                     Pattern, Match,
58                     partial, partialmethod, cached_property,
59                     AbstractContextManager, AbstractAsyncContextManager,
60                     Awaitable, Coroutine,
61                     AsyncIterable, AsyncIterator,
62                     AsyncGenerator, Generator,
63                     Iterable, Iterator,
64                     Reversible,
65                     Container, Collection,
66                     Mailbox, _PartialFile,
67                     ContextVar, Token,
68                     Field,
69                     Set, MutableSet,
70                     Mapping, MutableMapping, MappingView,
71                     KeysView, ItemsView, ValuesView,
72                     Sequence, MutableSequence,
73                     MappingProxyType, AsyncGeneratorType,
74                     DirEntry,
75                     chain,
76                     TemporaryDirectory, SpooledTemporaryFile,
77                     Queue, SimpleQueue,
78                     _AssertRaisesContext,
79                     SplitResult, ParseResult,
80                     ValueProxy, ApplyResult,
81                     WeakSet, ReferenceType, ref,
82                     ShareableList, MPSimpleQueue,
83                     Future, _WorkItem,
84                     Morsel]
85    if ctypes is not None:
86        generic_types.extend((ctypes.Array, ctypes.LibraryLoader))
87
88    def test_subscriptable(self):
89        for t in self.generic_types:
90            if t is None:
91                continue
92            tname = t.__name__
93            with self.subTest(f"Testing {tname}"):
94                alias = t[int]
95                self.assertIs(alias.__origin__, t)
96                self.assertEqual(alias.__args__, (int,))
97                self.assertEqual(alias.__parameters__, ())
98
99    def test_unsubscriptable(self):
100        for t in int, str, float, Sized, Hashable:
101            tname = t.__name__
102            with self.subTest(f"Testing {tname}"):
103                with self.assertRaises(TypeError):
104                    t[int]
105
106    def test_instantiate(self):
107        for t in tuple, list, dict, set, frozenset, defaultdict, deque:
108            tname = t.__name__
109            with self.subTest(f"Testing {tname}"):
110                alias = t[int]
111                self.assertEqual(alias(), t())
112                if t is dict:
113                    self.assertEqual(alias(iter([('a', 1), ('b', 2)])), dict(a=1, b=2))
114                    self.assertEqual(alias(a=1, b=2), dict(a=1, b=2))
115                elif t is defaultdict:
116                    def default():
117                        return 'value'
118                    a = alias(default)
119                    d = defaultdict(default)
120                    self.assertEqual(a['test'], d['test'])
121                else:
122                    self.assertEqual(alias(iter((1, 2, 3))), t((1, 2, 3)))
123
124    def test_unbound_methods(self):
125        t = list[int]
126        a = t()
127        t.append(a, 'foo')
128        self.assertEqual(a, ['foo'])
129        x = t.__getitem__(a, 0)
130        self.assertEqual(x, 'foo')
131        self.assertEqual(t.__len__(a), 1)
132
133    def test_subclassing(self):
134        class C(list[int]):
135            pass
136        self.assertEqual(C.__bases__, (list,))
137        self.assertEqual(C.__class__, type)
138
139    def test_class_methods(self):
140        t = dict[int, None]
141        self.assertEqual(dict.fromkeys(range(2)), {0: None, 1: None})  # This works
142        self.assertEqual(t.fromkeys(range(2)), {0: None, 1: None})  # Should be equivalent
143
144    def test_no_chaining(self):
145        t = list[int]
146        with self.assertRaises(TypeError):
147            t[int]
148
149    def test_generic_subclass(self):
150        class MyList(list):
151            pass
152        t = MyList[int]
153        self.assertIs(t.__origin__, MyList)
154        self.assertEqual(t.__args__, (int,))
155        self.assertEqual(t.__parameters__, ())
156
157    def test_repr(self):
158        class MyList(list):
159            pass
160        self.assertEqual(repr(list[str]), 'list[str]')
161        self.assertEqual(repr(list[()]), 'list[()]')
162        self.assertEqual(repr(tuple[int, ...]), 'tuple[int, ...]')
163        self.assertTrue(repr(MyList[int]).endswith('.BaseTest.test_repr.<locals>.MyList[int]'))
164        self.assertEqual(repr(list[str]()), '[]')  # instances should keep their normal repr
165
166    def test_exposed_type(self):
167        import types
168        a = types.GenericAlias(list, int)
169        self.assertEqual(str(a), 'list[int]')
170        self.assertIs(a.__origin__, list)
171        self.assertEqual(a.__args__, (int,))
172        self.assertEqual(a.__parameters__, ())
173
174    def test_parameters(self):
175        from typing import List, Dict, Callable
176        D0 = dict[str, int]
177        self.assertEqual(D0.__args__, (str, int))
178        self.assertEqual(D0.__parameters__, ())
179        D1a = dict[str, V]
180        self.assertEqual(D1a.__args__, (str, V))
181        self.assertEqual(D1a.__parameters__, (V,))
182        D1b = dict[K, int]
183        self.assertEqual(D1b.__args__, (K, int))
184        self.assertEqual(D1b.__parameters__, (K,))
185        D2a = dict[K, V]
186        self.assertEqual(D2a.__args__, (K, V))
187        self.assertEqual(D2a.__parameters__, (K, V))
188        D2b = dict[T, T]
189        self.assertEqual(D2b.__args__, (T, T))
190        self.assertEqual(D2b.__parameters__, (T,))
191        L0 = list[str]
192        self.assertEqual(L0.__args__, (str,))
193        self.assertEqual(L0.__parameters__, ())
194        L1 = list[T]
195        self.assertEqual(L1.__args__, (T,))
196        self.assertEqual(L1.__parameters__, (T,))
197        L2 = list[list[T]]
198        self.assertEqual(L2.__args__, (list[T],))
199        self.assertEqual(L2.__parameters__, (T,))
200        L3 = list[List[T]]
201        self.assertEqual(L3.__args__, (List[T],))
202        self.assertEqual(L3.__parameters__, (T,))
203        L4a = list[Dict[K, V]]
204        self.assertEqual(L4a.__args__, (Dict[K, V],))
205        self.assertEqual(L4a.__parameters__, (K, V))
206        L4b = list[Dict[T, int]]
207        self.assertEqual(L4b.__args__, (Dict[T, int],))
208        self.assertEqual(L4b.__parameters__, (T,))
209        L5 = list[Callable[[K, V], K]]
210        self.assertEqual(L5.__args__, (Callable[[K, V], K],))
211        self.assertEqual(L5.__parameters__, (K, V))
212
213    def test_parameter_chaining(self):
214        from typing import List, Dict, Union, Callable
215        self.assertEqual(list[T][int], list[int])
216        self.assertEqual(dict[str, T][int], dict[str, int])
217        self.assertEqual(dict[T, int][str], dict[str, int])
218        self.assertEqual(dict[K, V][str, int], dict[str, int])
219        self.assertEqual(dict[T, T][int], dict[int, int])
220
221        self.assertEqual(list[list[T]][int], list[list[int]])
222        self.assertEqual(list[dict[T, int]][str], list[dict[str, int]])
223        self.assertEqual(list[dict[str, T]][int], list[dict[str, int]])
224        self.assertEqual(list[dict[K, V]][str, int], list[dict[str, int]])
225        self.assertEqual(dict[T, list[int]][str], dict[str, list[int]])
226
227        self.assertEqual(list[List[T]][int], list[List[int]])
228        self.assertEqual(list[Dict[K, V]][str, int], list[Dict[str, int]])
229        self.assertEqual(list[Union[K, V]][str, int], list[Union[str, int]])
230        self.assertEqual(list[Callable[[K, V], K]][str, int],
231                         list[Callable[[str, int], str]])
232        self.assertEqual(dict[T, List[int]][str], dict[str, List[int]])
233
234        with self.assertRaises(TypeError):
235            list[int][int]
236            dict[T, int][str, int]
237            dict[str, T][str, int]
238            dict[T, T][str, int]
239
240    def test_equality(self):
241        self.assertEqual(list[int], list[int])
242        self.assertEqual(dict[str, int], dict[str, int])
243        self.assertNotEqual(dict[str, int], dict[str, str])
244        self.assertNotEqual(list, list[int])
245        self.assertNotEqual(list[int], list)
246
247    def test_isinstance(self):
248        self.assertTrue(isinstance([], list))
249        with self.assertRaises(TypeError):
250            isinstance([], list[str])
251
252    def test_issubclass(self):
253        class L(list): ...
254        self.assertTrue(issubclass(L, list))
255        with self.assertRaises(TypeError):
256            issubclass(L, list[str])
257
258    def test_type_generic(self):
259        t = type[int]
260        Test = t('Test', (), {})
261        self.assertTrue(isinstance(Test, type))
262        test = Test()
263        self.assertEqual(t(test), Test)
264        self.assertEqual(t(0), int)
265
266    def test_type_subclass_generic(self):
267        class MyType(type):
268            pass
269        with self.assertRaises(TypeError):
270            MyType[int]
271
272    def test_pickle(self):
273        alias = GenericAlias(list, T)
274        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
275            s = pickle.dumps(alias, proto)
276            loaded = pickle.loads(s)
277            self.assertEqual(loaded.__origin__, alias.__origin__)
278            self.assertEqual(loaded.__args__, alias.__args__)
279            self.assertEqual(loaded.__parameters__, alias.__parameters__)
280
281    def test_copy(self):
282        class X(list):
283            def __copy__(self):
284                return self
285            def __deepcopy__(self, memo):
286                return self
287
288        for origin in list, deque, X:
289            alias = GenericAlias(origin, T)
290            copied = copy.copy(alias)
291            self.assertEqual(copied.__origin__, alias.__origin__)
292            self.assertEqual(copied.__args__, alias.__args__)
293            self.assertEqual(copied.__parameters__, alias.__parameters__)
294            copied = copy.deepcopy(alias)
295            self.assertEqual(copied.__origin__, alias.__origin__)
296            self.assertEqual(copied.__args__, alias.__args__)
297            self.assertEqual(copied.__parameters__, alias.__parameters__)
298
299    def test_union(self):
300        a = typing.Union[list[int], list[str]]
301        self.assertEqual(a.__args__, (list[int], list[str]))
302        self.assertEqual(a.__parameters__, ())
303
304    def test_union_generic(self):
305        a = typing.Union[list[T], tuple[T, ...]]
306        self.assertEqual(a.__args__, (list[T], tuple[T, ...]))
307        self.assertEqual(a.__parameters__, (T,))
308
309    def test_dir(self):
310        dir_of_gen_alias = set(dir(list[int]))
311        self.assertTrue(dir_of_gen_alias.issuperset(dir(list)))
312        for generic_alias_property in ("__origin__", "__args__", "__parameters__"):
313            self.assertIn(generic_alias_property, dir_of_gen_alias)
314
315    def test_weakref(self):
316        for t in self.generic_types:
317            if t is None:
318                continue
319            tname = t.__name__
320            with self.subTest(f"Testing {tname}"):
321                alias = t[int]
322                self.assertEqual(ref(alias)(), alias)
323
324    def test_no_kwargs(self):
325        # bpo-42576
326        with self.assertRaises(TypeError):
327            GenericAlias(bad=float)
328
329    def test_subclassing_types_genericalias(self):
330        class SubClass(GenericAlias): ...
331        alias = SubClass(list, int)
332        class Bad(GenericAlias):
333            def __new__(cls, *args, **kwargs):
334                super().__new__(cls, *args, **kwargs)
335
336        self.assertEqual(alias, list[int])
337        with self.assertRaises(TypeError):
338            Bad(list, int, bad=int)
339
340
341if __name__ == "__main__":
342    unittest.main()
343