File: seq_tests.py

package info (click to toggle)
domdf-python-tools 3.10.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,780 kB
  • sloc: python: 10,838; makefile: 7
file content (572 lines) | stat: -rw-r--r-- 13,441 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
#  From https://raw.githubusercontent.com/python/cpython/master/Lib/test/seq_tests.py
#  Licensed under the Python Software Foundation License Version 2.
#  Copyright © 2001-2020 Python Software Foundation. All rights reserved.
#  Copyright © 2000 BeOpen.com. All rights reserved.
#  Copyright © 1995-2000 Corporation for National Research Initiatives. All rights reserved.
#  Copyright © 1991-1995 Stichting Mathematisch Centrum. All rights reserved.
"""
Tests common to tuple, list and UserList.UserList
"""

# stdlib
import pickle
import sys
from itertools import chain
from typing import Any, List

# 3rd party
import pytest
from coincidence.selectors import not_pypy

# this package
from domdf_python_tools.compat import PYPY38_PLUS
from domdf_python_tools.iterative import Len


class _ALWAYS_EQ:
	"""
	Object that is equal to anything.
	"""

	def __eq__(self, other):
		return True

	def __ne__(self, other):
		return False


ALWAYS_EQ = _ALWAYS_EQ()


class _NEVER_EQ:
	"""
	Object that is not equal to anything.
	"""

	def __eq__(self, other):
		return False

	def __ne__(self, other):
		return True

	def __hash__(self):
		return 1


NEVER_EQ = _NEVER_EQ()


# Various iterables
# This is used for checking the constructor (here and in test_deque.py)
def iterfunc(seqn):
	"""
	Regular generator.
	"""

	yield from seqn


class Sequence:
	"""
	Sequence using ``__getitem__``.
	"""

	def __init__(self, seqn):
		self.seqn = seqn

	def __getitem__(self, i):
		return self.seqn[i]


class IterFunc:
	"""
	Sequence using iterator protocol,
	"""

	def __init__(self, seqn):
		self.seqn = seqn
		self.i = 0

	def __iter__(self):
		return self

	def __next__(self):
		if self.i >= len(self.seqn):
			raise StopIteration
		v = self.seqn[self.i]
		self.i += 1
		return v


class IterGen:
	"""
	Sequence using iterator protocol defined with a generator.
	"""

	def __init__(self, seqn):
		self.seqn = seqn
		self.i = 0

	def __iter__(self):
		yield from self.seqn


class IterNextOnly:
	"""
	Missing __getitem__ and __iter__.
	"""

	def __init__(self, seqn):
		self.seqn = seqn
		self.i = 0

	def __next__(self):
		if self.i >= len(self.seqn):
			raise StopIteration
		v = self.seqn[self.i]
		self.i += 1
		return v


class IterNoNext:
	"""
	Iterator missing __next__().
	"""

	def __init__(self, seqn):
		self.seqn = seqn
		self.i = 0

	def __iter__(self):
		return self


class IterGenExc:
	"""
	Test propagation of exceptions.
	"""

	def __init__(self, seqn):
		self.seqn = seqn
		self.i = 0

	def __iter__(self):
		return self

	def __next__(self):
		3 // 0  # pylint: disable=pointless-statement


class IterFuncStop:
	"""
	Test immediate stop.
	"""

	def __init__(self, seqn):
		pass

	def __iter__(self):
		return self

	def __next__(self):
		raise StopIteration


def itermulti(seqn):
	"""
	Test multiple tiers of iterators.
	"""

	return chain(map(lambda x: x, iterfunc(IterGen(Sequence(seqn)))))


class LyingTuple(tuple):
	__slots__ = ()

	def __iter__(self):
		yield 1


class LyingList(list):

	def __iter__(self):
		yield 1


class CommonTest:
	# The type to be tested
	type2test: type

	def assertEqual(self, left, right):
		assert left == right

	def assertNotEqual(self, left, right):
		assert left != right

	def assertRaises(self, what, func, *args):
		with pytest.raises(what):
			func(*args)

	def test_constructors(self):
		l0: List = []
		l1 = [0]
		l2 = [0, 1]

		u = self.type2test()
		u0 = self.type2test(l0)
		u1 = self.type2test(l1)
		u2 = self.type2test(l2)

		uu = self.type2test(u)
		uu0 = self.type2test(u0)
		uu1 = self.type2test(u1)
		uu2 = self.type2test(u2)

		v = self.type2test(tuple(u))

		class OtherSeq:

			def __init__(self, initseq):
				self.__data = initseq

			def __len__(self):
				return len(self.__data)

			def __getitem__(self, i):
				return self.__data[i]

		s = OtherSeq(u0)
		v0 = self.type2test(s)
		assert len(v0) == len(s)

		s2 = "this is also a sequence"
		vv = self.type2test(s2)
		assert len(vv) == len(s2)

		# Create from various iteratables
		for s2 in ("123", '', range(1000), ("do", 1.2), range(2000, 2200, 5)):  # type: ignore
			for g in (Sequence, IterFunc, IterGen, itermulti, iterfunc):
				assert self.type2test(g(s2)) == self.type2test(s2)
			assert self.type2test(IterFuncStop(s2)) == self.type2test()
			assert self.type2test(c for c in "123") == self.type2test("123")
			with pytest.raises(TypeError):
				self.type2test(IterNextOnly(s2))
			with pytest.raises(TypeError):
				self.type2test(IterNoNext(s2))
			with pytest.raises(ZeroDivisionError):
				self.type2test(IterGenExc(s2))

		# Issue #23757
		assert self.type2test(LyingTuple((2, ))) == self.type2test((1, ))
		assert self.type2test(LyingList([2])) == self.type2test([1])

	def test_truth(self):
		assert not self.type2test()
		assert self.type2test([42])

	def test_getitem(self):
		u = self.type2test([0, 1, 2, 3, 4])
		for i in Len(u):
			assert u[i] == i
			assert u[int(i)] == i
		for i in range(-len(u), -1):
			assert u[i] == len(u) + i
			assert u[int(i)] == len(u) + i
		with pytest.raises(IndexError):
			u.__getitem__(-len(u) - 1)
		with pytest.raises(IndexError):
			u.__getitem__(len(u))
		with pytest.raises(ValueError):
			u.__getitem__(slice(0, 10, 0))

		u = self.type2test()
		with pytest.raises(IndexError):
			u.__getitem__(0)
		with pytest.raises(IndexError):
			u.__getitem__(-1)

		with pytest.raises(TypeError):
			u.__getitem__()

		a = self.type2test([10, 11])
		assert a[0] == 10
		assert a[1] == 11
		assert a[-2] == 10
		assert a[-1] == 11
		with pytest.raises(IndexError):
			a.__getitem__(-3)
		with pytest.raises(IndexError):
			a.__getitem__(3)

	def test_getslice(self):
		l = [0, 1, 2, 3, 4]
		u = self.type2test(l)

		assert u[0:0] == self.type2test()
		assert u[1:2] == self.type2test([1])
		assert u[-2:-1] == self.type2test([3])
		assert u[-1000:1000] == u
		assert u[1000:-1000] == self.type2test([])
		assert u[:] == u
		assert u[1:None] == self.type2test([1, 2, 3, 4])
		assert u[None:3] == self.type2test([0, 1, 2])

		# Extended slices
		assert u[::] == u
		assert u[::2] == self.type2test([0, 2, 4])
		assert u[1::2] == self.type2test([1, 3])
		assert u[::-1] == self.type2test([4, 3, 2, 1, 0])
		assert u[::-2] == self.type2test([4, 2, 0])
		assert u[3::-2] == self.type2test([3, 1])
		assert u[3:3:-2] == self.type2test([])
		assert u[3:2:-2] == self.type2test([3])
		assert u[3:1:-2] == self.type2test([3])
		assert u[3:0:-2] == self.type2test([3, 1])
		assert u[::-100] == self.type2test([4])
		assert u[100:-100:] == self.type2test([])
		assert u[-100:100:] == u
		assert u[100:-100:-1] == u[::-1]
		assert u[-100:100:-1] == self.type2test([])
		assert u[-100:100:2] == self.type2test([0, 2, 4])

		# Test extreme cases with long ints
		a = self.type2test([0, 1, 2, 3, 4])
		assert a[-pow(2, 128):3] == self.type2test([0, 1, 2])
		assert a[3:pow(2, 145)] == self.type2test([3, 4])
		assert a[3::sys.maxsize] == self.type2test([3])

	def test_contains(self):
		u = self.type2test([0, 1, 2])
		for i in u:
			assert i in u
		for i in min(u) - 1, max(u) + 1:
			assert i not in u

		with pytest.raises(TypeError):
			u.__contains__()

	def test_contains_fake(self):
		# Sequences must use rich comparison against each item
		# (unless "is" is true, or an earlier item answered)
		# So ALWAYS_EQ must be found in all non-empty sequences.
		assert ALWAYS_EQ not in self.type2test([])
		assert ALWAYS_EQ in self.type2test([1])
		assert 1 in self.type2test([ALWAYS_EQ])
		assert NEVER_EQ not in self.type2test([])

	def test_contains_order(self):
		# Sequences must test in-order.  If a rich comparison has side
		# effects, these will be visible to tests against later members.
		# In this test, the "side effect" is a short-circuiting raise.
		class DoNotTestEq(Exception):
			pass

		class StopCompares:

			def __eq__(self, other):
				raise DoNotTestEq

		checkfirst = self.type2test([1, StopCompares()])
		assert 1 in checkfirst
		checklast = self.type2test([StopCompares(), 1])
		with pytest.raises(DoNotTestEq):
			checklast.__contains__(1)

	def test_len(self):
		assert len(self.type2test()) == 0
		assert len(self.type2test([])) == 0
		assert len(self.type2test([0])) == 1
		assert len(self.type2test([0, 1, 2])) == 3

	def test_minmax(self):
		u = self.type2test([0, 1, 2])
		assert min(u) == 0
		assert max(u) == 2

	def test_addmul(self):
		u1 = self.type2test([0])
		u2 = self.type2test([0, 1])
		assert u1 == u1 + self.type2test()
		assert u1 == self.type2test() + u1
		assert u1 + self.type2test([1]) == u2
		assert self.type2test([-1]) + u1 == self.type2test([-1, 0])
		assert self.type2test() == u2 * 0
		assert self.type2test() == 0 * u2
		assert self.type2test() == u2 * 0
		assert self.type2test() == 0 * u2
		assert u2 == u2 * 1
		assert u2 == 1 * u2
		assert u2 == u2 * 1
		assert u2 == 1 * u2
		assert u2 + u2 == u2 * 2
		assert u2 + u2 == 2 * u2
		assert u2 + u2 == u2 * 2
		assert u2 + u2 == 2 * u2
		assert u2 + u2 + u2 == u2 * 3
		assert u2 + u2 + u2 == 3 * u2

		class subclass(self.type2test):  # type: ignore
			pass

		u3 = subclass([0, 1])
		assert u3 == u3 * 1
		assert u3 is not u3 * 1

	def test_iadd(self):
		u = self.type2test([0, 1])
		u += self.type2test()
		assert u == self.type2test([0, 1])
		u += self.type2test([2, 3])
		assert u == self.type2test([0, 1, 2, 3])
		u += self.type2test([4, 5])
		assert u == self.type2test([0, 1, 2, 3, 4, 5])

		u = self.type2test("spam")
		u += self.type2test("eggs")
		assert u == self.type2test("spameggs")

	def test_imul(self):
		u = self.type2test([0, 1])
		u *= 3
		assert u == self.type2test([0, 1, 0, 1, 0, 1])
		u *= 0
		assert u == self.type2test([])

	def test_getitemoverwriteiter(self):
		# Verify that __getitem__ overrides are not recognized by __iter__
		class T(self.type2test):  # type: ignore

			def __getitem__(self, key: Any) -> str:
				return str(key) + "!!!"

		assert next(iter(T((1, 2)))) == 1

	def test_repeat(self):
		for m in range(4):
			s = tuple(range(m))
			for n in range(-3, 5):
				assert self.type2test(s * n) == self.type2test(s) * n
			assert self.type2test(s) * (-4) == self.type2test([])
			assert id(s) == id(s * 1)

	def test_bigrepeat(self):
		if sys.maxsize <= 2147483647:
			x = self.type2test([0])
			x *= 2**16
			with pytest.raises(MemoryError):
				x.__mul__(2**16)
			if hasattr(x, "__imul__"):
				with pytest.raises(MemoryError):
					x.__imul__(2**16)

	def test_subscript(self):
		a = self.type2test([10, 11])
		assert a.__getitem__(0) == 10
		assert a.__getitem__(1) == 11
		assert a.__getitem__(-2) == 10
		assert a.__getitem__(-1) == 11
		with pytest.raises(IndexError):
			a.__getitem__(-3)
		with pytest.raises(IndexError):
			a.__getitem__(3)
		assert a.__getitem__(slice(0, 1)) == self.type2test([10])
		assert a.__getitem__(slice(1, 2)) == self.type2test([11])
		assert a.__getitem__(slice(0, 2)) == self.type2test([10, 11])
		assert a.__getitem__(slice(0, 3)) == self.type2test([10, 11])
		assert a.__getitem__(slice(3, 5)) == self.type2test([])
		with pytest.raises(ValueError):
			a.__getitem__(slice(0, 10, 0))
		with pytest.raises(TypeError):
			a.__getitem__('x')

	def test_count(self):
		a = self.type2test([0, 1, 2]) * 3
		assert a.count(0) == 3
		assert a.count(1) == 3
		assert a.count(3) == 0

		assert a.count(ALWAYS_EQ), 9
		assert self.type2test([ALWAYS_EQ, ALWAYS_EQ]).count(1) == 2

		if not PYPY38_PLUS:  # TODO: figure out why the tests fail
			assert self.type2test([ALWAYS_EQ, ALWAYS_EQ]).count(NEVER_EQ) == 2
			assert self.type2test([NEVER_EQ, NEVER_EQ]).count(ALWAYS_EQ) == 0

		with pytest.raises(TypeError):
			a.count()

		class BadExc(Exception):
			pass

		class BadCmp:

			def __eq__(self, other):
				if other == 2:
					raise BadExc()
				return False

		with pytest.raises(BadExc):
			a.count(BadCmp())

	@not_pypy("Doesn't work on PyPy")
	def test_index(self):
		u = self.type2test([0, 1])
		assert u.index(0) == 0
		assert u.index(1) == 1
		with pytest.raises(ValueError):
			u.index(2)

		u = self.type2test([-2, -1, 0, 0, 1, 2])
		assert u.count(0) == 2
		assert u.index(0) == 2
		assert u.index(0, 2) == 2
		assert u.index(-2, -10) == 0
		assert u.index(0, 3) == 3
		assert u.index(0, 3, 4) == 3
		with pytest.raises(ValueError):
			u.index(2, 0, -10)

		assert u.index(ALWAYS_EQ) == 0
		assert self.type2test([ALWAYS_EQ, ALWAYS_EQ]).index(1) == 0
		assert self.type2test([ALWAYS_EQ, ALWAYS_EQ]).index(NEVER_EQ) == 0
		with pytest.raises(ValueError):
			self.type2test([NEVER_EQ, NEVER_EQ]).index(ALWAYS_EQ)

		with pytest.raises(TypeError):
			u.index()

		class BadExc(Exception):
			pass

		class BadCmp:

			def __eq__(self, other):
				if other == 2:
					raise BadExc()
				return False

		a = self.type2test([0, 1, 2, 3])
		with pytest.raises(BadExc):
			a.index(BadCmp())

		a = self.type2test([-2, -1, 0, 0, 1, 2])
		assert a.index(0) == 2
		assert a.index(0, 2) == 2
		assert a.index(0, -4) == 2
		assert a.index(-2, -10) == 0
		assert a.index(0, 3) == 3
		assert a.index(0, -3) == 3
		assert a.index(0, 3, 4) == 3
		assert a.index(0, -3, -2) == 3
		assert a.index(0, -4 * sys.maxsize, 4 * sys.maxsize) == 2
		with pytest.raises(ValueError):
			a.index(0, 4 * sys.maxsize, -4 * sys.maxsize)
		with pytest.raises(ValueError):
			a.index(2, 0, -10)

	def test_pickle(self):
		lst = self.type2test([4, 5, 6, 7])
		for proto in range(pickle.HIGHEST_PROTOCOL + 1):
			lst2 = pickle.loads(pickle.dumps(lst, proto))
			assert lst2 == lst
			assert id(lst2) != id(lst)