Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(182748)

Side by Side Diff: Lib/test/test_functools.py

Issue 14373: C implementation of functools.lru_cache
Patch Set: Created 6 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 import collections 1 import collections
2 import sys 2 import sys
3 import unittest 3 import unittest
4 from test import support 4 from test import support
5 from weakref import proxy 5 from weakref import proxy
6 import pickle 6 import pickle
7 from random import choice 7 from random import choice
8 8
9 import functools 9 import functools
10 10
(...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after
644 if isinstance(other, TestTO): 644 if isinstance(other, TestTO):
645 return self.value == other.value 645 return self.value == other.value
646 return False 646 return False
647 def __lt__(self, other): 647 def __lt__(self, other):
648 if isinstance(other, TestTO): 648 if isinstance(other, TestTO):
649 return self.value < other.value 649 return self.value < other.value
650 raise TypeError 650 raise TypeError
651 with self.assertRaises(TypeError): 651 with self.assertRaises(TypeError):
652 TestTO(8) <= () 652 TestTO(8) <= ()
653 653
654 class TestLRU(unittest.TestCase): 654 class TestLRU:
655 655
656 def test_lru(self): 656 def test_lru(self):
657 def orig(x, y): 657 def orig(x, y):
658 return 3 * x + y 658 return 3 * x + y
659 f = functools.lru_cache(maxsize=20)(orig) 659 f = self.module.lru_cache(maxsize=20)(orig)
660 hits, misses, maxsize, currsize = f.cache_info() 660 hits, misses, maxsize, currsize = f.cache_info()
661 self.assertEqual(maxsize, 20) 661 self.assertEqual(maxsize, 20)
662 self.assertEqual(currsize, 0) 662 self.assertEqual(currsize, 0)
663 self.assertEqual(hits, 0) 663 self.assertEqual(hits, 0)
664 self.assertEqual(misses, 0) 664 self.assertEqual(misses, 0)
665 665
666 domain = range(5) 666 domain = range(5)
667 for i in range(1000): 667 for i in range(1000):
668 x, y = choice(domain), choice(domain) 668 x, y = choice(domain), choice(domain)
669 actual = f(x, y) 669 actual = f(x, y)
(...skipping 17 matching lines...) Expand all
687 687
688 # Test bypassing the cache 688 # Test bypassing the cache
689 self.assertIs(f.__wrapped__, orig) 689 self.assertIs(f.__wrapped__, orig)
690 f.__wrapped__(x, y) 690 f.__wrapped__(x, y)
691 hits, misses, maxsize, currsize = f.cache_info() 691 hits, misses, maxsize, currsize = f.cache_info()
692 self.assertEqual(hits, 0) 692 self.assertEqual(hits, 0)
693 self.assertEqual(misses, 1) 693 self.assertEqual(misses, 1)
694 self.assertEqual(currsize, 1) 694 self.assertEqual(currsize, 1)
695 695
696 # test size zero (which means "never-cache") 696 # test size zero (which means "never-cache")
697 @functools.lru_cache(0) 697 @self.module.lru_cache(0)
698 def f(): 698 def f():
699 nonlocal f_cnt 699 nonlocal f_cnt
700 f_cnt += 1 700 f_cnt += 1
701 return 20 701 return 20
702 self.assertEqual(f.cache_info().maxsize, 0) 702 self.assertEqual(f.cache_info().maxsize, 0)
703 f_cnt = 0 703 f_cnt = 0
704 for i in range(5): 704 for i in range(5):
705 self.assertEqual(f(), 20) 705 self.assertEqual(f(), 20)
706 self.assertEqual(f_cnt, 5) 706 self.assertEqual(f_cnt, 5)
707 hits, misses, maxsize, currsize = f.cache_info() 707 hits, misses, maxsize, currsize = f.cache_info()
708 self.assertEqual(hits, 0) 708 self.assertEqual(hits, 0)
709 self.assertEqual(misses, 5) 709 self.assertEqual(misses, 5)
710 self.assertEqual(currsize, 0) 710 self.assertEqual(currsize, 0)
711 711
712 # test size one 712 # test size one
713 @functools.lru_cache(1) 713 @self.module.lru_cache(1)
714 def f(): 714 def f():
715 nonlocal f_cnt 715 nonlocal f_cnt
716 f_cnt += 1 716 f_cnt += 1
717 return 20 717 return 20
718 self.assertEqual(f.cache_info().maxsize, 1) 718 self.assertEqual(f.cache_info().maxsize, 1)
719 f_cnt = 0 719 f_cnt = 0
720 for i in range(5): 720 for i in range(5):
721 self.assertEqual(f(), 20) 721 self.assertEqual(f(), 20)
722 self.assertEqual(f_cnt, 1) 722 self.assertEqual(f_cnt, 1)
723 hits, misses, maxsize, currsize = f.cache_info() 723 hits, misses, maxsize, currsize = f.cache_info()
724 self.assertEqual(hits, 4) 724 self.assertEqual(hits, 4)
725 self.assertEqual(misses, 1) 725 self.assertEqual(misses, 1)
726 self.assertEqual(currsize, 1) 726 self.assertEqual(currsize, 1)
727 727
728 # test size two 728 # test size two
729 @functools.lru_cache(2) 729 @self.module.lru_cache(2)
730 def f(x): 730 def f(x):
731 nonlocal f_cnt 731 nonlocal f_cnt
732 f_cnt += 1 732 f_cnt += 1
733 return x*10 733 return x*10
734 self.assertEqual(f.cache_info().maxsize, 2) 734 self.assertEqual(f.cache_info().maxsize, 2)
735 f_cnt = 0 735 f_cnt = 0
736 for x in 7, 9, 7, 9, 7, 9, 8, 8, 8, 9, 9, 9, 8, 8, 8, 7: 736 for x in 7, 9, 7, 9, 7, 9, 8, 8, 8, 9, 9, 9, 8, 8, 8, 7:
737 # * * * * 737 # * * * *
738 self.assertEqual(f(x), x*10) 738 self.assertEqual(f(x), x*10)
739 self.assertEqual(f_cnt, 4) 739 self.assertEqual(f_cnt, 4)
740 hits, misses, maxsize, currsize = f.cache_info() 740 hits, misses, maxsize, currsize = f.cache_info()
741 self.assertEqual(hits, 12) 741 self.assertEqual(hits, 12)
742 self.assertEqual(misses, 4) 742 self.assertEqual(misses, 4)
743 self.assertEqual(currsize, 2) 743 self.assertEqual(currsize, 2)
744 744
745 def test_lru_with_maxsize_none(self): 745 def test_lru_with_maxsize_none(self):
746 @functools.lru_cache(maxsize=None) 746 @self.module.lru_cache(maxsize=None)
747 def fib(n): 747 def fib(n):
748 if n < 2: 748 if n < 2:
749 return n 749 return n
750 return fib(n-1) + fib(n-2) 750 return fib(n-1) + fib(n-2)
751 self.assertEqual([fib(n) for n in range(16)], 751 self.assertEqual([fib(n) for n in range(16)],
752 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]) 752 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
753 self.assertEqual(fib.cache_info(), 753 self.assertEqual(fib.cache_info(),
754 functools._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)) 754 self.module._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16 ))
755 fib.cache_clear() 755 fib.cache_clear()
756 self.assertEqual(fib.cache_info(), 756 self.assertEqual(fib.cache_info(),
757 functools._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)) 757 self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
758 758
759 def test_lru_with_exceptions(self): 759 def test_lru_with_exceptions(self):
760 # Verify that user_function exceptions get passed through without 760 # Verify that user_function exceptions get passed through without
761 # creating a hard-to-read chained exception. 761 # creating a hard-to-read chained exception.
762 # http://bugs.python.org/issue13177 762 # http://bugs.python.org/issue13177
763 for maxsize in (None, 128): 763 for maxsize in (None, 128):
764 @functools.lru_cache(maxsize) 764 @self.module.lru_cache(maxsize)
765 def func(i): 765 def func(i):
766 return 'abc'[i] 766 return 'abc'[i]
767 self.assertEqual(func(0), 'a') 767 self.assertEqual(func(0), 'a')
768 with self.assertRaises(IndexError) as cm: 768 with self.assertRaises(IndexError) as cm:
769 func(15) 769 func(15)
770 self.assertIsNone(cm.exception.__context__) 770 self.assertIsNone(cm.exception.__context__)
771 # Verify that the previous exception did not result in a cached entr y 771 # Verify that the previous exception did not result in a cached entr y
772 with self.assertRaises(IndexError): 772 with self.assertRaises(IndexError):
773 func(15) 773 func(15)
774 774
775 def test_lru_with_types(self): 775 def test_lru_with_types(self):
776 for maxsize in (None, 128): 776 for maxsize in (None, 128):
777 @functools.lru_cache(maxsize=maxsize, typed=True) 777 @self.module.lru_cache(maxsize=maxsize, typed=True)
778 def square(x): 778 def square(x):
779 return x * x 779 return x * x
780 self.assertEqual(square(3), 9) 780 self.assertEqual(square(3), 9)
781 self.assertEqual(type(square(3)), type(9)) 781 self.assertEqual(type(square(3)), type(9))
782 self.assertEqual(square(3.0), 9.0) 782 self.assertEqual(square(3.0), 9.0)
783 self.assertEqual(type(square(3.0)), type(9.0)) 783 self.assertEqual(type(square(3.0)), type(9.0))
784 self.assertEqual(square(x=3), 9) 784 self.assertEqual(square(x=3), 9)
785 self.assertEqual(type(square(x=3)), type(9)) 785 self.assertEqual(type(square(x=3)), type(9))
786 self.assertEqual(square(x=3.0), 9.0) 786 self.assertEqual(square(x=3.0), 9.0)
787 self.assertEqual(type(square(x=3.0)), type(9.0)) 787 self.assertEqual(type(square(x=3.0)), type(9.0))
788 self.assertEqual(square.cache_info().hits, 4) 788 self.assertEqual(square.cache_info().hits, 4)
789 self.assertEqual(square.cache_info().misses, 4) 789 self.assertEqual(square.cache_info().misses, 4)
790 790
791 def test_lru_with_keyword_args(self): 791 def test_lru_with_keyword_args(self):
792 @functools.lru_cache() 792 @self.module.lru_cache()
793 def fib(n): 793 def fib(n):
794 if n < 2: 794 if n < 2:
795 return n 795 return n
796 return fib(n=n-1) + fib(n=n-2) 796 return fib(n=n-1) + fib(n=n-2)
797 self.assertEqual( 797 self.assertEqual(
798 [fib(n=number) for number in range(16)], 798 [fib(n=number) for number in range(16)],
799 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610] 799 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
800 ) 800 )
801 self.assertEqual(fib.cache_info(), 801 self.assertEqual(fib.cache_info(),
802 functools._CacheInfo(hits=28, misses=16, maxsize=128, currsize=16)) 802 self.module._CacheInfo(hits=28, misses=16, maxsize=128, currsize=16) )
803 fib.cache_clear() 803 fib.cache_clear()
804 self.assertEqual(fib.cache_info(), 804 self.assertEqual(fib.cache_info(),
805 functools._CacheInfo(hits=0, misses=0, maxsize=128, currsize=0)) 805 self.module._CacheInfo(hits=0, misses=0, maxsize=128, currsize=0))
806 806
807 def test_lru_with_keyword_args_maxsize_none(self): 807 def test_lru_with_keyword_args_maxsize_none(self):
808 @functools.lru_cache(maxsize=None) 808 @self.module.lru_cache(maxsize=None)
809 def fib(n): 809 def fib(n):
810 if n < 2: 810 if n < 2:
811 return n 811 return n
812 return fib(n=n-1) + fib(n=n-2) 812 return fib(n=n-1) + fib(n=n-2)
813 self.assertEqual([fib(n=number) for number in range(16)], 813 self.assertEqual([fib(n=number) for number in range(16)],
814 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]) 814 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
815 self.assertEqual(fib.cache_info(), 815 self.assertEqual(fib.cache_info(),
816 functools._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)) 816 self.module._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16 ))
817 fib.cache_clear() 817 fib.cache_clear()
818 self.assertEqual(fib.cache_info(), 818 self.assertEqual(fib.cache_info(),
819 functools._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)) 819 self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
820
821 def test_lru_cache_decoration(self):
822 def f(zomg: 'zomg_annotation'):
823 """f doc string"""
824 return 42
825 g = self.module.lru_cache()(f)
826 for attr in self.module.WRAPPER_ASSIGNMENTS:
827 self.assertEqual(getattr(g, attr), getattr(f, attr))
828
829 def test_lru_cache_threaded(self):
storchaka 2012/12/30 18:09:43 The test should be skipped if threading is not ava
830 from threading import Thread
831 from operator import methodcaller
832
833 def orig(x, y):
834 return 3 * x + y
835 f = self.module.lru_cache(maxsize=20)(orig)
836 hits, misses, maxsize, currsize = f.cache_info()
837 self.assertEqual(currsize, 0)
838
839 def full(f, *args):
840 for _ in range(10):
841 f(*args)
842
843 def clear(f):
844 for _ in range(10):
845 f.cache_clear()
846
847 # create 5 threads in order to fill cache
848 threads = []
849 for k in range(5):
850 t = Thread(target=full, args=[f, k, k])
851 threads.append(t)
852 t.start()
853
854 map(methodcaller("join"), threads)
storchaka 2012/12/30 18:09:43 What a strange construction? Actually it even does
855
856 hits, misses, maxsize, currsize = f.cache_info()
857 self.assertEqual(hits, 45)
858 self.assertEqual(misses, 5)
859 self.assertEqual(currsize, 5)
860
861 # create 5 threads in order to fill cache and 1 to clear it
862 cleaner = Thread(target=clear, args=[f])
863 threads = [cleaner]
864 for k in range(5):
865 t = Thread(target=full, args=[f, k, k])
866 threads.append(t)
867 t.start()
868
869 map(methodcaller("join"), threads)
870
storchaka 2012/12/30 18:09:43 And what now?
871 class TestLRUC(BaseTestC, TestLRU):
872 pass
873
874 class TestLRUPy(BaseTestPy, TestLRU):
875 pass
820 876
821 def test_main(verbose=None): 877 def test_main(verbose=None):
822 test_classes = ( 878 test_classes = (
823 TestPartialC, 879 TestPartialC,
824 TestPartialPy, 880 TestPartialPy,
825 TestPartialCSubclass, 881 TestPartialCSubclass,
826 TestPartialPySubclass, 882 TestPartialPySubclass,
827 TestUpdateWrapper, 883 TestUpdateWrapper,
828 TestTotalOrdering, 884 TestTotalOrdering,
829 TestCmpToKeyC, 885 TestCmpToKeyC,
830 TestCmpToKeyPy, 886 TestCmpToKeyPy,
831 TestWraps, 887 TestWraps,
832 TestReduce, 888 TestReduce,
833 TestLRU, 889 TestLRUC,
890 TestLRUPy
834 ) 891 )
835 support.run_unittest(*test_classes) 892 support.run_unittest(*test_classes)
836 893
837 # verify reference counting 894 # verify reference counting
838 if verbose and hasattr(sys, "gettotalrefcount"): 895 if verbose and hasattr(sys, "gettotalrefcount"):
839 import gc 896 import gc
840 counts = [None] * 5 897 counts = [None] * 5
841 for i in range(len(counts)): 898 for i in range(len(counts)):
842 support.run_unittest(*test_classes) 899 support.run_unittest(*test_classes)
843 gc.collect() 900 gc.collect()
844 counts[i] = sys.gettotalrefcount() 901 counts[i] = sys.gettotalrefcount()
845 print(counts) 902 print(counts)
846 903
847 if __name__ == '__main__': 904 if __name__ == '__main__':
848 test_main(verbose=True) 905 test_main(verbose=True)
OLDNEW
« no previous file with comments | « Lib/functools.py ('k') | Modules/_functoolsmodule.c » ('j') | Modules/_functoolsmodule.c » ('J')

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+