Back to index

python3.2  3.2.2
test_functools.py
Go to the documentation of this file.
00001 import functools
00002 import collections
00003 import sys
00004 import unittest
00005 from test import support
00006 from weakref import proxy
00007 import pickle
00008 from random import choice
00009 
00010 @staticmethod
00011 def PythonPartial(func, *args, **keywords):
00012     'Pure Python approximation of partial()'
00013     def newfunc(*fargs, **fkeywords):
00014         newkeywords = keywords.copy()
00015         newkeywords.update(fkeywords)
00016         return func(*(args + fargs), **newkeywords)
00017     newfunc.func = func
00018     newfunc.args = args
00019     newfunc.keywords = keywords
00020     return newfunc
00021 
00022 def capture(*args, **kw):
00023     """capture all positional and keyword arguments"""
00024     return args, kw
00025 
00026 def signature(part):
00027     """ return the signature of a partial object """
00028     return (part.func, part.args, part.keywords, part.__dict__)
00029 
00030 class TestPartial(unittest.TestCase):
00031 
00032     thetype = functools.partial
00033 
00034     def test_basic_examples(self):
00035         p = self.thetype(capture, 1, 2, a=10, b=20)
00036         self.assertEqual(p(3, 4, b=30, c=40),
00037                          ((1, 2, 3, 4), dict(a=10, b=30, c=40)))
00038         p = self.thetype(map, lambda x: x*10)
00039         self.assertEqual(list(p([1,2,3,4])), [10, 20, 30, 40])
00040 
00041     def test_attributes(self):
00042         p = self.thetype(capture, 1, 2, a=10, b=20)
00043         # attributes should be readable
00044         self.assertEqual(p.func, capture)
00045         self.assertEqual(p.args, (1, 2))
00046         self.assertEqual(p.keywords, dict(a=10, b=20))
00047         # attributes should not be writable
00048         if not isinstance(self.thetype, type):
00049             return
00050         self.assertRaises(AttributeError, setattr, p, 'func', map)
00051         self.assertRaises(AttributeError, setattr, p, 'args', (1, 2))
00052         self.assertRaises(AttributeError, setattr, p, 'keywords', dict(a=1, b=2))
00053 
00054         p = self.thetype(hex)
00055         try:
00056             del p.__dict__
00057         except TypeError:
00058             pass
00059         else:
00060             self.fail('partial object allowed __dict__ to be deleted')
00061 
00062     def test_argument_checking(self):
00063         self.assertRaises(TypeError, self.thetype)     # need at least a func arg
00064         try:
00065             self.thetype(2)()
00066         except TypeError:
00067             pass
00068         else:
00069             self.fail('First arg not checked for callability')
00070 
00071     def test_protection_of_callers_dict_argument(self):
00072         # a caller's dictionary should not be altered by partial
00073         def func(a=10, b=20):
00074             return a
00075         d = {'a':3}
00076         p = self.thetype(func, a=5)
00077         self.assertEqual(p(**d), 3)
00078         self.assertEqual(d, {'a':3})
00079         p(b=7)
00080         self.assertEqual(d, {'a':3})
00081 
00082     def test_arg_combinations(self):
00083         # exercise special code paths for zero args in either partial
00084         # object or the caller
00085         p = self.thetype(capture)
00086         self.assertEqual(p(), ((), {}))
00087         self.assertEqual(p(1,2), ((1,2), {}))
00088         p = self.thetype(capture, 1, 2)
00089         self.assertEqual(p(), ((1,2), {}))
00090         self.assertEqual(p(3,4), ((1,2,3,4), {}))
00091 
00092     def test_kw_combinations(self):
00093         # exercise special code paths for no keyword args in
00094         # either the partial object or the caller
00095         p = self.thetype(capture)
00096         self.assertEqual(p(), ((), {}))
00097         self.assertEqual(p(a=1), ((), {'a':1}))
00098         p = self.thetype(capture, a=1)
00099         self.assertEqual(p(), ((), {'a':1}))
00100         self.assertEqual(p(b=2), ((), {'a':1, 'b':2}))
00101         # keyword args in the call override those in the partial object
00102         self.assertEqual(p(a=3, b=2), ((), {'a':3, 'b':2}))
00103 
00104     def test_positional(self):
00105         # make sure positional arguments are captured correctly
00106         for args in [(), (0,), (0,1), (0,1,2), (0,1,2,3)]:
00107             p = self.thetype(capture, *args)
00108             expected = args + ('x',)
00109             got, empty = p('x')
00110             self.assertTrue(expected == got and empty == {})
00111 
00112     def test_keyword(self):
00113         # make sure keyword arguments are captured correctly
00114         for a in ['a', 0, None, 3.5]:
00115             p = self.thetype(capture, a=a)
00116             expected = {'a':a,'x':None}
00117             empty, got = p(x=None)
00118             self.assertTrue(expected == got and empty == ())
00119 
00120     def test_no_side_effects(self):
00121         # make sure there are no side effects that affect subsequent calls
00122         p = self.thetype(capture, 0, a=1)
00123         args1, kw1 = p(1, b=2)
00124         self.assertTrue(args1 == (0,1) and kw1 == {'a':1,'b':2})
00125         args2, kw2 = p()
00126         self.assertTrue(args2 == (0,) and kw2 == {'a':1})
00127 
00128     def test_error_propagation(self):
00129         def f(x, y):
00130             x / y
00131         self.assertRaises(ZeroDivisionError, self.thetype(f, 1, 0))
00132         self.assertRaises(ZeroDivisionError, self.thetype(f, 1), 0)
00133         self.assertRaises(ZeroDivisionError, self.thetype(f), 1, 0)
00134         self.assertRaises(ZeroDivisionError, self.thetype(f, y=0), 1)
00135 
00136     def test_weakref(self):
00137         f = self.thetype(int, base=16)
00138         p = proxy(f)
00139         self.assertEqual(f.func, p.func)
00140         f = None
00141         self.assertRaises(ReferenceError, getattr, p, 'func')
00142 
00143     def test_with_bound_and_unbound_methods(self):
00144         data = list(map(str, range(10)))
00145         join = self.thetype(str.join, '')
00146         self.assertEqual(join(data), '0123456789')
00147         join = self.thetype(''.join)
00148         self.assertEqual(join(data), '0123456789')
00149 
00150     def test_repr(self):
00151         args = (object(), object())
00152         args_repr = ', '.join(repr(a) for a in args)
00153         kwargs = {'a': object(), 'b': object()}
00154         kwargs_repr = ', '.join("%s=%r" % (k, v) for k, v in kwargs.items())
00155         if self.thetype is functools.partial:
00156             name = 'functools.partial'
00157         else:
00158             name = self.thetype.__name__
00159 
00160         f = self.thetype(capture)
00161         self.assertEqual('{}({!r})'.format(name, capture),
00162                          repr(f))
00163 
00164         f = self.thetype(capture, *args)
00165         self.assertEqual('{}({!r}, {})'.format(name, capture, args_repr),
00166                          repr(f))
00167 
00168         f = self.thetype(capture, **kwargs)
00169         self.assertEqual('{}({!r}, {})'.format(name, capture, kwargs_repr),
00170                          repr(f))
00171 
00172         f = self.thetype(capture, *args, **kwargs)
00173         self.assertEqual('{}({!r}, {}, {})'.format(name, capture, args_repr, kwargs_repr),
00174                          repr(f))
00175 
00176     def test_pickle(self):
00177         f = self.thetype(signature, 'asdf', bar=True)
00178         f.add_something_to__dict__ = True
00179         f_copy = pickle.loads(pickle.dumps(f))
00180         self.assertEqual(signature(f), signature(f_copy))
00181 
00182 class PartialSubclass(functools.partial):
00183     pass
00184 
00185 class TestPartialSubclass(TestPartial):
00186 
00187     thetype = PartialSubclass
00188 
00189 class TestPythonPartial(TestPartial):
00190 
00191     thetype = PythonPartial
00192 
00193     # the python version hasn't a nice repr
00194     def test_repr(self): pass
00195 
00196     # the python version isn't picklable
00197     def test_pickle(self): pass
00198 
00199 class TestUpdateWrapper(unittest.TestCase):
00200 
00201     def check_wrapper(self, wrapper, wrapped,
00202                       assigned=functools.WRAPPER_ASSIGNMENTS,
00203                       updated=functools.WRAPPER_UPDATES):
00204         # Check attributes were assigned
00205         for name in assigned:
00206             self.assertTrue(getattr(wrapper, name) is getattr(wrapped, name))
00207         # Check attributes were updated
00208         for name in updated:
00209             wrapper_attr = getattr(wrapper, name)
00210             wrapped_attr = getattr(wrapped, name)
00211             for key in wrapped_attr:
00212                 self.assertTrue(wrapped_attr[key] is wrapper_attr[key])
00213 
00214     def _default_update(self):
00215         def f(a:'This is a new annotation'):
00216             """This is a test"""
00217             pass
00218         f.attr = 'This is also a test'
00219         def wrapper(b:'This is the prior annotation'):
00220             pass
00221         functools.update_wrapper(wrapper, f)
00222         return wrapper, f
00223 
00224     def test_default_update(self):
00225         wrapper, f = self._default_update()
00226         self.check_wrapper(wrapper, f)
00227         self.assertIs(wrapper.__wrapped__, f)
00228         self.assertEqual(wrapper.__name__, 'f')
00229         self.assertEqual(wrapper.attr, 'This is also a test')
00230         self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation')
00231         self.assertNotIn('b', wrapper.__annotations__)
00232 
00233     @unittest.skipIf(sys.flags.optimize >= 2,
00234                      "Docstrings are omitted with -O2 and above")
00235     def test_default_update_doc(self):
00236         wrapper, f = self._default_update()
00237         self.assertEqual(wrapper.__doc__, 'This is a test')
00238 
00239     def test_no_update(self):
00240         def f():
00241             """This is a test"""
00242             pass
00243         f.attr = 'This is also a test'
00244         def wrapper():
00245             pass
00246         functools.update_wrapper(wrapper, f, (), ())
00247         self.check_wrapper(wrapper, f, (), ())
00248         self.assertEqual(wrapper.__name__, 'wrapper')
00249         self.assertEqual(wrapper.__doc__, None)
00250         self.assertEqual(wrapper.__annotations__, {})
00251         self.assertFalse(hasattr(wrapper, 'attr'))
00252 
00253     def test_selective_update(self):
00254         def f():
00255             pass
00256         f.attr = 'This is a different test'
00257         f.dict_attr = dict(a=1, b=2, c=3)
00258         def wrapper():
00259             pass
00260         wrapper.dict_attr = {}
00261         assign = ('attr',)
00262         update = ('dict_attr',)
00263         functools.update_wrapper(wrapper, f, assign, update)
00264         self.check_wrapper(wrapper, f, assign, update)
00265         self.assertEqual(wrapper.__name__, 'wrapper')
00266         self.assertEqual(wrapper.__doc__, None)
00267         self.assertEqual(wrapper.attr, 'This is a different test')
00268         self.assertEqual(wrapper.dict_attr, f.dict_attr)
00269 
00270     def test_missing_attributes(self):
00271         def f():
00272             pass
00273         def wrapper():
00274             pass
00275         wrapper.dict_attr = {}
00276         assign = ('attr',)
00277         update = ('dict_attr',)
00278         # Missing attributes on wrapped object are ignored
00279         functools.update_wrapper(wrapper, f, assign, update)
00280         self.assertNotIn('attr', wrapper.__dict__)
00281         self.assertEqual(wrapper.dict_attr, {})
00282         # Wrapper must have expected attributes for updating
00283         del wrapper.dict_attr
00284         with self.assertRaises(AttributeError):
00285             functools.update_wrapper(wrapper, f, assign, update)
00286         wrapper.dict_attr = 1
00287         with self.assertRaises(AttributeError):
00288             functools.update_wrapper(wrapper, f, assign, update)
00289 
00290     @unittest.skipIf(sys.flags.optimize >= 2,
00291                      "Docstrings are omitted with -O2 and above")
00292     def test_builtin_update(self):
00293         # Test for bug #1576241
00294         def wrapper():
00295             pass
00296         functools.update_wrapper(wrapper, max)
00297         self.assertEqual(wrapper.__name__, 'max')
00298         self.assertTrue(wrapper.__doc__.startswith('max('))
00299         self.assertEqual(wrapper.__annotations__, {})
00300 
00301 class TestWraps(TestUpdateWrapper):
00302 
00303     def _default_update(self):
00304         def f():
00305             """This is a test"""
00306             pass
00307         f.attr = 'This is also a test'
00308         @functools.wraps(f)
00309         def wrapper():
00310             pass
00311         self.check_wrapper(wrapper, f)
00312         return wrapper
00313 
00314     def test_default_update(self):
00315         wrapper = self._default_update()
00316         self.assertEqual(wrapper.__name__, 'f')
00317         self.assertEqual(wrapper.attr, 'This is also a test')
00318 
00319     @unittest.skipIf(not sys.flags.optimize <= 1,
00320                      "Docstrings are omitted with -O2 and above")
00321     def test_default_update_doc(self):
00322         wrapper = self._default_update()
00323         self.assertEqual(wrapper.__doc__, 'This is a test')
00324 
00325     def test_no_update(self):
00326         def f():
00327             """This is a test"""
00328             pass
00329         f.attr = 'This is also a test'
00330         @functools.wraps(f, (), ())
00331         def wrapper():
00332             pass
00333         self.check_wrapper(wrapper, f, (), ())
00334         self.assertEqual(wrapper.__name__, 'wrapper')
00335         self.assertEqual(wrapper.__doc__, None)
00336         self.assertFalse(hasattr(wrapper, 'attr'))
00337 
00338     def test_selective_update(self):
00339         def f():
00340             pass
00341         f.attr = 'This is a different test'
00342         f.dict_attr = dict(a=1, b=2, c=3)
00343         def add_dict_attr(f):
00344             f.dict_attr = {}
00345             return f
00346         assign = ('attr',)
00347         update = ('dict_attr',)
00348         @functools.wraps(f, assign, update)
00349         @add_dict_attr
00350         def wrapper():
00351             pass
00352         self.check_wrapper(wrapper, f, assign, update)
00353         self.assertEqual(wrapper.__name__, 'wrapper')
00354         self.assertEqual(wrapper.__doc__, None)
00355         self.assertEqual(wrapper.attr, 'This is a different test')
00356         self.assertEqual(wrapper.dict_attr, f.dict_attr)
00357 
00358 class TestReduce(unittest.TestCase):
00359     func = functools.reduce
00360 
00361     def test_reduce(self):
00362         class Squares:
00363             def __init__(self, max):
00364                 self.max = max
00365                 self.sofar = []
00366 
00367             def __len__(self):
00368                 return len(self.sofar)
00369 
00370             def __getitem__(self, i):
00371                 if not 0 <= i < self.max: raise IndexError
00372                 n = len(self.sofar)
00373                 while n <= i:
00374                     self.sofar.append(n*n)
00375                     n += 1
00376                 return self.sofar[i]
00377         def add(x, y):
00378             return x + y
00379         self.assertEqual(self.func(add, ['a', 'b', 'c'], ''), 'abc')
00380         self.assertEqual(
00381             self.func(add, [['a', 'c'], [], ['d', 'w']], []),
00382             ['a','c','d','w']
00383         )
00384         self.assertEqual(self.func(lambda x, y: x*y, range(2,8), 1), 5040)
00385         self.assertEqual(
00386             self.func(lambda x, y: x*y, range(2,21), 1),
00387             2432902008176640000
00388         )
00389         self.assertEqual(self.func(add, Squares(10)), 285)
00390         self.assertEqual(self.func(add, Squares(10), 0), 285)
00391         self.assertEqual(self.func(add, Squares(0), 0), 0)
00392         self.assertRaises(TypeError, self.func)
00393         self.assertRaises(TypeError, self.func, 42, 42)
00394         self.assertRaises(TypeError, self.func, 42, 42, 42)
00395         self.assertEqual(self.func(42, "1"), "1") # func is never called with one item
00396         self.assertEqual(self.func(42, "", "1"), "1") # func is never called with one item
00397         self.assertRaises(TypeError, self.func, 42, (42, 42))
00398         self.assertRaises(TypeError, self.func, add, []) # arg 2 must not be empty sequence with no initial value
00399         self.assertRaises(TypeError, self.func, add, "")
00400         self.assertRaises(TypeError, self.func, add, ())
00401         self.assertRaises(TypeError, self.func, add, object())
00402 
00403         class TestFailingIter:
00404             def __iter__(self):
00405                 raise RuntimeError
00406         self.assertRaises(RuntimeError, self.func, add, TestFailingIter())
00407 
00408         self.assertEqual(self.func(add, [], None), None)
00409         self.assertEqual(self.func(add, [], 42), 42)
00410 
00411         class BadSeq:
00412             def __getitem__(self, index):
00413                 raise ValueError
00414         self.assertRaises(ValueError, self.func, 42, BadSeq())
00415 
00416     # Test reduce()'s use of iterators.
00417     def test_iterator_usage(self):
00418         class SequenceClass:
00419             def __init__(self, n):
00420                 self.n = n
00421             def __getitem__(self, i):
00422                 if 0 <= i < self.n:
00423                     return i
00424                 else:
00425                     raise IndexError
00426 
00427         from operator import add
00428         self.assertEqual(self.func(add, SequenceClass(5)), 10)
00429         self.assertEqual(self.func(add, SequenceClass(5), 42), 52)
00430         self.assertRaises(TypeError, self.func, add, SequenceClass(0))
00431         self.assertEqual(self.func(add, SequenceClass(0), 42), 42)
00432         self.assertEqual(self.func(add, SequenceClass(1)), 0)
00433         self.assertEqual(self.func(add, SequenceClass(1), 42), 42)
00434 
00435         d = {"one": 1, "two": 2, "three": 3}
00436         self.assertEqual(self.func(add, d), "".join(d.keys()))
00437 
00438 class TestCmpToKey(unittest.TestCase):
00439     def test_cmp_to_key(self):
00440         def mycmp(x, y):
00441             return y - x
00442         self.assertEqual(sorted(range(5), key=functools.cmp_to_key(mycmp)),
00443                          [4, 3, 2, 1, 0])
00444 
00445     def test_hash(self):
00446         def mycmp(x, y):
00447             return y - x
00448         key = functools.cmp_to_key(mycmp)
00449         k = key(10)
00450         self.assertRaises(TypeError, hash, k)
00451         self.assertFalse(isinstance(k, collections.Hashable))
00452 
00453 class TestTotalOrdering(unittest.TestCase):
00454 
00455     def test_total_ordering_lt(self):
00456         @functools.total_ordering
00457         class A:
00458             def __init__(self, value):
00459                 self.value = value
00460             def __lt__(self, other):
00461                 return self.value < other.value
00462             def __eq__(self, other):
00463                 return self.value == other.value
00464         self.assertTrue(A(1) < A(2))
00465         self.assertTrue(A(2) > A(1))
00466         self.assertTrue(A(1) <= A(2))
00467         self.assertTrue(A(2) >= A(1))
00468         self.assertTrue(A(2) <= A(2))
00469         self.assertTrue(A(2) >= A(2))
00470 
00471     def test_total_ordering_le(self):
00472         @functools.total_ordering
00473         class A:
00474             def __init__(self, value):
00475                 self.value = value
00476             def __le__(self, other):
00477                 return self.value <= other.value
00478             def __eq__(self, other):
00479                 return self.value == other.value
00480         self.assertTrue(A(1) < A(2))
00481         self.assertTrue(A(2) > A(1))
00482         self.assertTrue(A(1) <= A(2))
00483         self.assertTrue(A(2) >= A(1))
00484         self.assertTrue(A(2) <= A(2))
00485         self.assertTrue(A(2) >= A(2))
00486 
00487     def test_total_ordering_gt(self):
00488         @functools.total_ordering
00489         class A:
00490             def __init__(self, value):
00491                 self.value = value
00492             def __gt__(self, other):
00493                 return self.value > other.value
00494             def __eq__(self, other):
00495                 return self.value == other.value
00496         self.assertTrue(A(1) < A(2))
00497         self.assertTrue(A(2) > A(1))
00498         self.assertTrue(A(1) <= A(2))
00499         self.assertTrue(A(2) >= A(1))
00500         self.assertTrue(A(2) <= A(2))
00501         self.assertTrue(A(2) >= A(2))
00502 
00503     def test_total_ordering_ge(self):
00504         @functools.total_ordering
00505         class A:
00506             def __init__(self, value):
00507                 self.value = value
00508             def __ge__(self, other):
00509                 return self.value >= other.value
00510             def __eq__(self, other):
00511                 return self.value == other.value
00512         self.assertTrue(A(1) < A(2))
00513         self.assertTrue(A(2) > A(1))
00514         self.assertTrue(A(1) <= A(2))
00515         self.assertTrue(A(2) >= A(1))
00516         self.assertTrue(A(2) <= A(2))
00517         self.assertTrue(A(2) >= A(2))
00518 
00519     def test_total_ordering_no_overwrite(self):
00520         # new methods should not overwrite existing
00521         @functools.total_ordering
00522         class A(int):
00523             pass
00524         self.assertTrue(A(1) < A(2))
00525         self.assertTrue(A(2) > A(1))
00526         self.assertTrue(A(1) <= A(2))
00527         self.assertTrue(A(2) >= A(1))
00528         self.assertTrue(A(2) <= A(2))
00529         self.assertTrue(A(2) >= A(2))
00530 
00531     def test_no_operations_defined(self):
00532         with self.assertRaises(ValueError):
00533             @functools.total_ordering
00534             class A:
00535                 pass
00536 
00537     def test_bug_10042(self):
00538         @functools.total_ordering
00539         class TestTO:
00540             def __init__(self, value):
00541                 self.value = value
00542             def __eq__(self, other):
00543                 if isinstance(other, TestTO):
00544                     return self.value == other.value
00545                 return False
00546             def __lt__(self, other):
00547                 if isinstance(other, TestTO):
00548                     return self.value < other.value
00549                 raise TypeError
00550         with self.assertRaises(TypeError):
00551             TestTO(8) <= ()
00552 
00553 class TestLRU(unittest.TestCase):
00554 
00555     def test_lru(self):
00556         def orig(x, y):
00557             return 3*x+y
00558         f = functools.lru_cache(maxsize=20)(orig)
00559         hits, misses, maxsize, currsize = f.cache_info()
00560         self.assertEqual(maxsize, 20)
00561         self.assertEqual(currsize, 0)
00562         self.assertEqual(hits, 0)
00563         self.assertEqual(misses, 0)
00564 
00565         domain = range(5)
00566         for i in range(1000):
00567             x, y = choice(domain), choice(domain)
00568             actual = f(x, y)
00569             expected = orig(x, y)
00570             self.assertEqual(actual, expected)
00571         hits, misses, maxsize, currsize = f.cache_info()
00572         self.assertTrue(hits > misses)
00573         self.assertEqual(hits + misses, 1000)
00574         self.assertEqual(currsize, 20)
00575 
00576         f.cache_clear()   # test clearing
00577         hits, misses, maxsize, currsize = f.cache_info()
00578         self.assertEqual(hits, 0)
00579         self.assertEqual(misses, 0)
00580         self.assertEqual(currsize, 0)
00581         f(x, y)
00582         hits, misses, maxsize, currsize = f.cache_info()
00583         self.assertEqual(hits, 0)
00584         self.assertEqual(misses, 1)
00585         self.assertEqual(currsize, 1)
00586 
00587         # Test bypassing the cache
00588         self.assertIs(f.__wrapped__, orig)
00589         f.__wrapped__(x, y)
00590         hits, misses, maxsize, currsize = f.cache_info()
00591         self.assertEqual(hits, 0)
00592         self.assertEqual(misses, 1)
00593         self.assertEqual(currsize, 1)
00594 
00595         # test size zero (which means "never-cache")
00596         @functools.lru_cache(0)
00597         def f():
00598             nonlocal f_cnt
00599             f_cnt += 1
00600             return 20
00601         self.assertEqual(f.cache_info().maxsize, 0)
00602         f_cnt = 0
00603         for i in range(5):
00604             self.assertEqual(f(), 20)
00605         self.assertEqual(f_cnt, 5)
00606         hits, misses, maxsize, currsize = f.cache_info()
00607         self.assertEqual(hits, 0)
00608         self.assertEqual(misses, 5)
00609         self.assertEqual(currsize, 0)
00610 
00611         # test size one
00612         @functools.lru_cache(1)
00613         def f():
00614             nonlocal f_cnt
00615             f_cnt += 1
00616             return 20
00617         self.assertEqual(f.cache_info().maxsize, 1)
00618         f_cnt = 0
00619         for i in range(5):
00620             self.assertEqual(f(), 20)
00621         self.assertEqual(f_cnt, 1)
00622         hits, misses, maxsize, currsize = f.cache_info()
00623         self.assertEqual(hits, 4)
00624         self.assertEqual(misses, 1)
00625         self.assertEqual(currsize, 1)
00626 
00627         # test size two
00628         @functools.lru_cache(2)
00629         def f(x):
00630             nonlocal f_cnt
00631             f_cnt += 1
00632             return x*10
00633         self.assertEqual(f.cache_info().maxsize, 2)
00634         f_cnt = 0
00635         for x in 7, 9, 7, 9, 7, 9, 8, 8, 8, 9, 9, 9, 8, 8, 8, 7:
00636             #    *  *              *                          *
00637             self.assertEqual(f(x), x*10)
00638         self.assertEqual(f_cnt, 4)
00639         hits, misses, maxsize, currsize = f.cache_info()
00640         self.assertEqual(hits, 12)
00641         self.assertEqual(misses, 4)
00642         self.assertEqual(currsize, 2)
00643 
00644     def test_lru_with_maxsize_none(self):
00645         @functools.lru_cache(maxsize=None)
00646         def fib(n):
00647             if n < 2:
00648                 return n
00649             return fib(n-1) + fib(n-2)
00650         self.assertEqual([fib(n) for n in range(16)],
00651             [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
00652         self.assertEqual(fib.cache_info(),
00653             functools._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
00654         fib.cache_clear()
00655         self.assertEqual(fib.cache_info(),
00656             functools._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
00657 
00658 def test_main(verbose=None):
00659     test_classes = (
00660         TestPartial,
00661         TestPartialSubclass,
00662         TestPythonPartial,
00663         TestUpdateWrapper,
00664         TestTotalOrdering,
00665         TestCmpToKey,
00666         TestWraps,
00667         TestReduce,
00668         TestLRU,
00669     )
00670     support.run_unittest(*test_classes)
00671 
00672     # verify reference counting
00673     if verbose and hasattr(sys, "gettotalrefcount"):
00674         import gc
00675         counts = [None] * 5
00676         for i in range(len(counts)):
00677             support.run_unittest(*test_classes)
00678             gc.collect()
00679             counts[i] = sys.gettotalrefcount()
00680         print(counts)
00681 
00682 if __name__ == '__main__':
00683     test_main(verbose=True)