]> git.ipfire.org Git - thirdparty/Python/cpython.git/blob - Lib/test/test_class.py
4c1814142736e3a019d7206fd53dfbc18c46a8cd
[thirdparty/Python/cpython.git] / Lib / test / test_class.py
1 "Test the functionality of Python classes implementing operators."
2
3 import unittest
4
5 testmeths = [
6
7 # Binary operations
8 "add",
9 "radd",
10 "sub",
11 "rsub",
12 "mul",
13 "rmul",
14 "matmul",
15 "rmatmul",
16 "truediv",
17 "rtruediv",
18 "floordiv",
19 "rfloordiv",
20 "mod",
21 "rmod",
22 "divmod",
23 "rdivmod",
24 "pow",
25 "rpow",
26 "rshift",
27 "rrshift",
28 "lshift",
29 "rlshift",
30 "and",
31 "rand",
32 "or",
33 "ror",
34 "xor",
35 "rxor",
36
37 # List/dict operations
38 "contains",
39 "getitem",
40 "setitem",
41 "delitem",
42
43 # Unary operations
44 "neg",
45 "pos",
46 "abs",
47
48 # generic operations
49 "init",
50 ]
51
52 # These need to return something other than None
53 # "hash",
54 # "str",
55 # "repr",
56 # "int",
57 # "float",
58
59 # These are separate because they can influence the test of other methods.
60 # "getattr",
61 # "setattr",
62 # "delattr",
63
64 callLst = []
65 def trackCall(f):
66 def track(*args, **kwargs):
67 callLst.append((f.__name__, args))
68 return f(*args, **kwargs)
69 return track
70
71 statictests = """
72 @trackCall
73 def __hash__(self, *args):
74 return hash(id(self))
75
76 @trackCall
77 def __str__(self, *args):
78 return "AllTests"
79
80 @trackCall
81 def __repr__(self, *args):
82 return "AllTests"
83
84 @trackCall
85 def __int__(self, *args):
86 return 1
87
88 @trackCall
89 def __index__(self, *args):
90 return 1
91
92 @trackCall
93 def __float__(self, *args):
94 return 1.0
95
96 @trackCall
97 def __eq__(self, *args):
98 return True
99
100 @trackCall
101 def __ne__(self, *args):
102 return False
103
104 @trackCall
105 def __lt__(self, *args):
106 return False
107
108 @trackCall
109 def __le__(self, *args):
110 return True
111
112 @trackCall
113 def __gt__(self, *args):
114 return False
115
116 @trackCall
117 def __ge__(self, *args):
118 return True
119 """
120
121 # Synthesize all the other AllTests methods from the names in testmeths.
122
123 method_template = """\
124 @trackCall
125 def __%s__(self, *args):
126 pass
127 """
128
129 d = {}
130 exec(statictests, globals(), d)
131 for method in testmeths:
132 exec(method_template % method, globals(), d)
133 AllTests = type("AllTests", (object,), d)
134 del d, statictests, method, method_template
135
136 class ClassTests(unittest.TestCase):
137 def setUp(self):
138 callLst[:] = []
139
140 def assertCallStack(self, expected_calls):
141 actualCallList = callLst[:] # need to copy because the comparison below will add
142 # additional calls to callLst
143 if expected_calls != actualCallList:
144 self.fail("Expected call list:\n %s\ndoes not match actual call list\n %s" %
145 (expected_calls, actualCallList))
146
147 def testInit(self):
148 foo = AllTests()
149 self.assertCallStack([("__init__", (foo,))])
150
151 def testBinaryOps(self):
152 testme = AllTests()
153 # Binary operations
154
155 callLst[:] = []
156 testme + 1
157 self.assertCallStack([("__add__", (testme, 1))])
158
159 callLst[:] = []
160 1 + testme
161 self.assertCallStack([("__radd__", (testme, 1))])
162
163 callLst[:] = []
164 testme - 1
165 self.assertCallStack([("__sub__", (testme, 1))])
166
167 callLst[:] = []
168 1 - testme
169 self.assertCallStack([("__rsub__", (testme, 1))])
170
171 callLst[:] = []
172 testme * 1
173 self.assertCallStack([("__mul__", (testme, 1))])
174
175 callLst[:] = []
176 1 * testme
177 self.assertCallStack([("__rmul__", (testme, 1))])
178
179 callLst[:] = []
180 testme @ 1
181 self.assertCallStack([("__matmul__", (testme, 1))])
182
183 callLst[:] = []
184 1 @ testme
185 self.assertCallStack([("__rmatmul__", (testme, 1))])
186
187 callLst[:] = []
188 testme / 1
189 self.assertCallStack([("__truediv__", (testme, 1))])
190
191
192 callLst[:] = []
193 1 / testme
194 self.assertCallStack([("__rtruediv__", (testme, 1))])
195
196 callLst[:] = []
197 testme // 1
198 self.assertCallStack([("__floordiv__", (testme, 1))])
199
200
201 callLst[:] = []
202 1 // testme
203 self.assertCallStack([("__rfloordiv__", (testme, 1))])
204
205 callLst[:] = []
206 testme % 1
207 self.assertCallStack([("__mod__", (testme, 1))])
208
209 callLst[:] = []
210 1 % testme
211 self.assertCallStack([("__rmod__", (testme, 1))])
212
213
214 callLst[:] = []
215 divmod(testme,1)
216 self.assertCallStack([("__divmod__", (testme, 1))])
217
218 callLst[:] = []
219 divmod(1, testme)
220 self.assertCallStack([("__rdivmod__", (testme, 1))])
221
222 callLst[:] = []
223 testme ** 1
224 self.assertCallStack([("__pow__", (testme, 1))])
225
226 callLst[:] = []
227 1 ** testme
228 self.assertCallStack([("__rpow__", (testme, 1))])
229
230 callLst[:] = []
231 testme >> 1
232 self.assertCallStack([("__rshift__", (testme, 1))])
233
234 callLst[:] = []
235 1 >> testme
236 self.assertCallStack([("__rrshift__", (testme, 1))])
237
238 callLst[:] = []
239 testme << 1
240 self.assertCallStack([("__lshift__", (testme, 1))])
241
242 callLst[:] = []
243 1 << testme
244 self.assertCallStack([("__rlshift__", (testme, 1))])
245
246 callLst[:] = []
247 testme & 1
248 self.assertCallStack([("__and__", (testme, 1))])
249
250 callLst[:] = []
251 1 & testme
252 self.assertCallStack([("__rand__", (testme, 1))])
253
254 callLst[:] = []
255 testme | 1
256 self.assertCallStack([("__or__", (testme, 1))])
257
258 callLst[:] = []
259 1 | testme
260 self.assertCallStack([("__ror__", (testme, 1))])
261
262 callLst[:] = []
263 testme ^ 1
264 self.assertCallStack([("__xor__", (testme, 1))])
265
266 callLst[:] = []
267 1 ^ testme
268 self.assertCallStack([("__rxor__", (testme, 1))])
269
270 def testListAndDictOps(self):
271 testme = AllTests()
272
273 # List/dict operations
274
275 class Empty: pass
276
277 try:
278 1 in Empty()
279 self.fail('failed, should have raised TypeError')
280 except TypeError:
281 pass
282
283 callLst[:] = []
284 1 in testme
285 self.assertCallStack([('__contains__', (testme, 1))])
286
287 callLst[:] = []
288 testme[1]
289 self.assertCallStack([('__getitem__', (testme, 1))])
290
291 callLst[:] = []
292 testme[1] = 1
293 self.assertCallStack([('__setitem__', (testme, 1, 1))])
294
295 callLst[:] = []
296 del testme[1]
297 self.assertCallStack([('__delitem__', (testme, 1))])
298
299 callLst[:] = []
300 testme[:42]
301 self.assertCallStack([('__getitem__', (testme, slice(None, 42)))])
302
303 callLst[:] = []
304 testme[:42] = "The Answer"
305 self.assertCallStack([('__setitem__', (testme, slice(None, 42),
306 "The Answer"))])
307
308 callLst[:] = []
309 del testme[:42]
310 self.assertCallStack([('__delitem__', (testme, slice(None, 42)))])
311
312 callLst[:] = []
313 testme[2:1024:10]
314 self.assertCallStack([('__getitem__', (testme, slice(2, 1024, 10)))])
315
316 callLst[:] = []
317 testme[2:1024:10] = "A lot"
318 self.assertCallStack([('__setitem__', (testme, slice(2, 1024, 10),
319 "A lot"))])
320 callLst[:] = []
321 del testme[2:1024:10]
322 self.assertCallStack([('__delitem__', (testme, slice(2, 1024, 10)))])
323
324 callLst[:] = []
325 testme[:42, ..., :24:, 24, 100]
326 self.assertCallStack([('__getitem__', (testme, (slice(None, 42, None),
327 Ellipsis,
328 slice(None, 24, None),
329 24, 100)))])
330 callLst[:] = []
331 testme[:42, ..., :24:, 24, 100] = "Strange"
332 self.assertCallStack([('__setitem__', (testme, (slice(None, 42, None),
333 Ellipsis,
334 slice(None, 24, None),
335 24, 100), "Strange"))])
336 callLst[:] = []
337 del testme[:42, ..., :24:, 24, 100]
338 self.assertCallStack([('__delitem__', (testme, (slice(None, 42, None),
339 Ellipsis,
340 slice(None, 24, None),
341 24, 100)))])
342
343 def testUnaryOps(self):
344 testme = AllTests()
345
346 callLst[:] = []
347 -testme
348 self.assertCallStack([('__neg__', (testme,))])
349 callLst[:] = []
350 +testme
351 self.assertCallStack([('__pos__', (testme,))])
352 callLst[:] = []
353 abs(testme)
354 self.assertCallStack([('__abs__', (testme,))])
355 callLst[:] = []
356 int(testme)
357 self.assertCallStack([('__int__', (testme,))])
358 callLst[:] = []
359 float(testme)
360 self.assertCallStack([('__float__', (testme,))])
361 callLst[:] = []
362 oct(testme)
363 self.assertCallStack([('__index__', (testme,))])
364 callLst[:] = []
365 hex(testme)
366 self.assertCallStack([('__index__', (testme,))])
367
368
369 def testMisc(self):
370 testme = AllTests()
371
372 callLst[:] = []
373 hash(testme)
374 self.assertCallStack([('__hash__', (testme,))])
375
376 callLst[:] = []
377 repr(testme)
378 self.assertCallStack([('__repr__', (testme,))])
379
380 callLst[:] = []
381 str(testme)
382 self.assertCallStack([('__str__', (testme,))])
383
384 callLst[:] = []
385 testme == 1
386 self.assertCallStack([('__eq__', (testme, 1))])
387
388 callLst[:] = []
389 testme < 1
390 self.assertCallStack([('__lt__', (testme, 1))])
391
392 callLst[:] = []
393 testme > 1
394 self.assertCallStack([('__gt__', (testme, 1))])
395
396 callLst[:] = []
397 testme != 1
398 self.assertCallStack([('__ne__', (testme, 1))])
399
400 callLst[:] = []
401 1 == testme
402 self.assertCallStack([('__eq__', (1, testme))])
403
404 callLst[:] = []
405 1 < testme
406 self.assertCallStack([('__gt__', (1, testme))])
407
408 callLst[:] = []
409 1 > testme
410 self.assertCallStack([('__lt__', (1, testme))])
411
412 callLst[:] = []
413 1 != testme
414 self.assertCallStack([('__ne__', (1, testme))])
415
416
417 def testGetSetAndDel(self):
418 # Interfering tests
419 class ExtraTests(AllTests):
420 @trackCall
421 def __getattr__(self, *args):
422 return "SomeVal"
423
424 @trackCall
425 def __setattr__(self, *args):
426 pass
427
428 @trackCall
429 def __delattr__(self, *args):
430 pass
431
432 testme = ExtraTests()
433
434 callLst[:] = []
435 testme.spam
436 self.assertCallStack([('__getattr__', (testme, "spam"))])
437
438 callLst[:] = []
439 testme.eggs = "spam, spam, spam and ham"
440 self.assertCallStack([('__setattr__', (testme, "eggs",
441 "spam, spam, spam and ham"))])
442
443 callLst[:] = []
444 del testme.cardinal
445 self.assertCallStack([('__delattr__', (testme, "cardinal"))])
446
447 def testHasAttrString(self):
448 import sys
449 from test.support import import_helper
450 _testlimitedcapi = import_helper.import_module('_testlimitedcapi')
451
452 class A:
453 def __init__(self):
454 self.attr = 1
455
456 a = A()
457 self.assertEqual(_testlimitedcapi.object_hasattrstring(a, b"attr"), 1)
458 self.assertEqual(_testlimitedcapi.object_hasattrstring(a, b"noattr"), 0)
459 self.assertIsNone(sys.exception())
460
461 def testDel(self):
462 x = []
463
464 class DelTest:
465 def __del__(self):
466 x.append("crab people, crab people")
467 testme = DelTest()
468 del testme
469 import gc
470 gc.collect()
471 self.assertEqual(["crab people, crab people"], x)
472
473 def testBadTypeReturned(self):
474 # return values of some method are type-checked
475 class BadTypeClass:
476 def __int__(self):
477 return None
478 __float__ = __int__
479 __complex__ = __int__
480 __str__ = __int__
481 __repr__ = __int__
482 __bytes__ = __int__
483 __bool__ = __int__
484 __index__ = __int__
485 def index(x):
486 return [][x]
487
488 for f in [float, complex, str, repr, bytes, bin, oct, hex, bool, index]:
489 self.assertRaises(TypeError, f, BadTypeClass())
490
491 def testHashStuff(self):
492 # Test correct errors from hash() on objects with comparisons but
493 # no __hash__
494
495 class C0:
496 pass
497
498 hash(C0()) # This should work; the next two should raise TypeError
499
500 class C2:
501 def __eq__(self, other): return 1
502
503 self.assertRaises(TypeError, hash, C2())
504
505
506 def testSFBug532646(self):
507 # Test for SF bug 532646
508
509 class A:
510 pass
511 A.__call__ = A()
512 a = A()
513
514 try:
515 a() # This should not segfault
516 except RecursionError:
517 pass
518 else:
519 self.fail("Failed to raise RecursionError")
520
521 def testForExceptionsRaisedInInstanceGetattr2(self):
522 # Tests for exceptions raised in instance_getattr2().
523
524 def booh(self):
525 raise AttributeError("booh")
526
527 class A:
528 a = property(booh)
529 try:
530 A().a # Raised AttributeError: A instance has no attribute 'a'
531 except AttributeError as x:
532 if str(x) != "booh":
533 self.fail("attribute error for A().a got masked: %s" % x)
534
535 class E:
536 __eq__ = property(booh)
537 E() == E() # In debug mode, caused a C-level assert() to fail
538
539 class I:
540 __init__ = property(booh)
541 try:
542 # In debug mode, printed XXX undetected error and
543 # raises AttributeError
544 I()
545 except AttributeError:
546 pass
547 else:
548 self.fail("attribute error for I.__init__ got masked")
549
550 def assertNotOrderable(self, a, b):
551 with self.assertRaises(TypeError):
552 a < b
553 with self.assertRaises(TypeError):
554 a > b
555 with self.assertRaises(TypeError):
556 a <= b
557 with self.assertRaises(TypeError):
558 a >= b
559
560 def testHashComparisonOfMethods(self):
561 # Test comparison and hash of methods
562 class A:
563 def __init__(self, x):
564 self.x = x
565 def f(self):
566 pass
567 def g(self):
568 pass
569 def __eq__(self, other):
570 return True
571 def __hash__(self):
572 raise TypeError
573 class B(A):
574 pass
575
576 a1 = A(1)
577 a2 = A(1)
578 self.assertTrue(a1.f == a1.f)
579 self.assertFalse(a1.f != a1.f)
580 self.assertFalse(a1.f == a2.f)
581 self.assertTrue(a1.f != a2.f)
582 self.assertFalse(a1.f == a1.g)
583 self.assertTrue(a1.f != a1.g)
584 self.assertNotOrderable(a1.f, a1.f)
585 self.assertEqual(hash(a1.f), hash(a1.f))
586
587 self.assertFalse(A.f == a1.f)
588 self.assertTrue(A.f != a1.f)
589 self.assertFalse(A.f == A.g)
590 self.assertTrue(A.f != A.g)
591 self.assertTrue(B.f == A.f)
592 self.assertFalse(B.f != A.f)
593 self.assertNotOrderable(A.f, A.f)
594 self.assertEqual(hash(B.f), hash(A.f))
595
596 # the following triggers a SystemError in 2.4
597 a = A(hash(A.f)^(-1))
598 hash(a.f)
599
600 def testSetattrWrapperNameIntern(self):
601 # Issue #25794: __setattr__ should intern the attribute name
602 class A:
603 pass
604
605 def add(self, other):
606 return 'summa'
607
608 name = str(b'__add__', 'ascii') # shouldn't be optimized
609 self.assertIsNot(name, '__add__') # not interned
610 type.__setattr__(A, name, add)
611 self.assertEqual(A() + 1, 'summa')
612
613 name2 = str(b'__add__', 'ascii')
614 self.assertIsNot(name2, '__add__')
615 self.assertIsNot(name2, name)
616 type.__delattr__(A, name2)
617 with self.assertRaises(TypeError):
618 A() + 1
619
620 def testSetattrNonStringName(self):
621 class A:
622 pass
623
624 with self.assertRaises(TypeError):
625 type.__setattr__(A, b'x', None)
626
627 def testTypeAttributeAccessErrorMessages(self):
628 class A:
629 pass
630
631 error_msg = "type object 'A' has no attribute 'x'"
632 with self.assertRaisesRegex(AttributeError, error_msg):
633 A.x
634 with self.assertRaisesRegex(AttributeError, error_msg):
635 del A.x
636
637 def testObjectAttributeAccessErrorMessages(self):
638 class A:
639 pass
640 class B:
641 y = 0
642 __slots__ = ('z',)
643 class C:
644 __slots__ = ("y",)
645
646 def __setattr__(self, name, value) -> None:
647 if name == "z":
648 super().__setattr__("y", 1)
649 else:
650 super().__setattr__(name, value)
651
652 error_msg = "'A' object has no attribute 'x'"
653 with self.assertRaisesRegex(AttributeError, error_msg):
654 A().x
655 with self.assertRaisesRegex(AttributeError, error_msg):
656 del A().x
657
658 error_msg = "'B' object has no attribute 'x'"
659 with self.assertRaisesRegex(AttributeError, error_msg):
660 B().x
661 with self.assertRaisesRegex(AttributeError, error_msg):
662 del B().x
663 with self.assertRaisesRegex(
664 AttributeError,
665 "'B' object has no attribute 'x' and no __dict__ for setting new attributes"
666 ):
667 B().x = 0
668 with self.assertRaisesRegex(
669 AttributeError,
670 "'C' object has no attribute 'x'"
671 ):
672 C().x = 0
673
674 error_msg = "'B' object attribute 'y' is read-only"
675 with self.assertRaisesRegex(AttributeError, error_msg):
676 del B().y
677 with self.assertRaisesRegex(AttributeError, error_msg):
678 B().y = 0
679
680 error_msg = 'z'
681 with self.assertRaisesRegex(AttributeError, error_msg):
682 B().z
683 with self.assertRaisesRegex(AttributeError, error_msg):
684 del B().z
685
686 def testConstructorErrorMessages(self):
687 # bpo-31506: Improves the error message logic for object_new & object_init
688
689 # Class without any method overrides
690 class C:
691 pass
692
693 error_msg = r'C.__init__\(\) takes exactly one argument \(the instance to initialize\)'
694
695 with self.assertRaisesRegex(TypeError, r'C\(\) takes no arguments'):
696 C(42)
697
698 with self.assertRaisesRegex(TypeError, r'C\(\) takes no arguments'):
699 C.__new__(C, 42)
700
701 with self.assertRaisesRegex(TypeError, error_msg):
702 C().__init__(42)
703
704 with self.assertRaisesRegex(TypeError, r'C\(\) takes no arguments'):
705 object.__new__(C, 42)
706
707 with self.assertRaisesRegex(TypeError, error_msg):
708 object.__init__(C(), 42)
709
710 # Class with both `__init__` & `__new__` method overridden
711 class D:
712 def __new__(cls, *args, **kwargs):
713 super().__new__(cls, *args, **kwargs)
714 def __init__(self, *args, **kwargs):
715 super().__init__(*args, **kwargs)
716
717 error_msg = r'object.__new__\(\) takes exactly one argument \(the type to instantiate\)'
718
719 with self.assertRaisesRegex(TypeError, error_msg):
720 D(42)
721
722 with self.assertRaisesRegex(TypeError, error_msg):
723 D.__new__(D, 42)
724
725 with self.assertRaisesRegex(TypeError, error_msg):
726 object.__new__(D, 42)
727
728 # Class that only overrides __init__
729 class E:
730 def __init__(self, *args, **kwargs):
731 super().__init__(*args, **kwargs)
732
733 error_msg = r'object.__init__\(\) takes exactly one argument \(the instance to initialize\)'
734
735 with self.assertRaisesRegex(TypeError, error_msg):
736 E().__init__(42)
737
738 with self.assertRaisesRegex(TypeError, error_msg):
739 object.__init__(E(), 42)
740
741 def testClassWithExtCall(self):
742 class Meta(int):
743 def __init__(*args, **kwargs):
744 pass
745
746 def __new__(cls, name, bases, attrs, **kwargs):
747 return bases, kwargs
748
749 d = {'metaclass': Meta}
750
751 class A(**d): pass
752 self.assertEqual(A, ((), {}))
753 class A(0, 1, 2, 3, 4, 5, 6, 7, **d): pass
754 self.assertEqual(A, (tuple(range(8)), {}))
755 class A(0, *range(1, 8), **d, foo='bar'): pass
756 self.assertEqual(A, (tuple(range(8)), {'foo': 'bar'}))
757
758 def testClassCallRecursionLimit(self):
759 class C:
760 def __init__(self):
761 self.c = C()
762
763 with self.assertRaises(RecursionError):
764 C()
765
766 def add_one_level():
767 #Each call to C() consumes 2 levels, so offset by 1.
768 C()
769
770 with self.assertRaises(RecursionError):
771 add_one_level()
772
773 def testMetaclassCallOptimization(self):
774 calls = 0
775
776 class TypeMetaclass(type):
777 def __call__(cls, *args, **kwargs):
778 nonlocal calls
779 calls += 1
780 return type.__call__(cls, *args, **kwargs)
781
782 class Type(metaclass=TypeMetaclass):
783 def __init__(self, obj):
784 self._obj = obj
785
786 for i in range(100):
787 Type(i)
788 self.assertEqual(calls, 100)
789
790
791 from _testinternalcapi import has_inline_values
792
793 Py_TPFLAGS_MANAGED_DICT = (1 << 2)
794
795 class Plain:
796 pass
797
798
799 class WithAttrs:
800
801 def __init__(self):
802 self.a = 1
803 self.b = 2
804 self.c = 3
805 self.d = 4
806
807
808 class TestInlineValues(unittest.TestCase):
809
810 def test_flags(self):
811 self.assertEqual(Plain.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT)
812 self.assertEqual(WithAttrs.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT)
813
814 def test_has_inline_values(self):
815 c = Plain()
816 self.assertTrue(has_inline_values(c))
817 del c.__dict__
818 self.assertFalse(has_inline_values(c))
819
820 def test_instances(self):
821 self.assertTrue(has_inline_values(Plain()))
822 self.assertTrue(has_inline_values(WithAttrs()))
823
824 def test_inspect_dict(self):
825 for cls in (Plain, WithAttrs):
826 c = cls()
827 c.__dict__
828 self.assertTrue(has_inline_values(c))
829
830 def test_update_dict(self):
831 d = { "e": 5, "f": 6 }
832 for cls in (Plain, WithAttrs):
833 c = cls()
834 c.__dict__.update(d)
835 self.assertTrue(has_inline_values(c))
836
837 @staticmethod
838 def set_100(obj):
839 for i in range(100):
840 setattr(obj, f"a{i}", i)
841
842 def check_100(self, obj):
843 for i in range(100):
844 self.assertEqual(getattr(obj, f"a{i}"), i)
845
846 def test_many_attributes(self):
847 class C: pass
848 c = C()
849 self.assertTrue(has_inline_values(c))
850 self.set_100(c)
851 self.assertFalse(has_inline_values(c))
852 self.check_100(c)
853 c = C()
854 self.assertTrue(has_inline_values(c))
855
856 def test_many_attributes_with_dict(self):
857 class C: pass
858 c = C()
859 d = c.__dict__
860 self.assertTrue(has_inline_values(c))
861 self.set_100(c)
862 self.assertFalse(has_inline_values(c))
863 self.check_100(c)
864
865
866
867 if __name__ == '__main__':
868 unittest.main()