blob: 8f48e9e6f4b2597a58e30f50b0933eebb43c1b23 [file] [log] [blame]
Thomas Wouters4d70c3d2006-06-08 14:42:34 +00001import functools
R. David Murray378c0cf2010-02-24 01:46:21 +00002import sys
Raymond Hettinger9c323f82005-02-28 19:39:44 +00003import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00004from test import support
Raymond Hettingerc8b6d1b2005-03-08 06:14:50 +00005from weakref import proxy
Jack Diederiche0cbd692009-04-01 04:27:09 +00006import pickle
Georg Brandl2e7346a2010-07-31 18:09:23 +00007from random import choice
Raymond Hettinger9c323f82005-02-28 19:39:44 +00008
9@staticmethod
10def PythonPartial(func, *args, **keywords):
11 'Pure Python approximation of partial()'
12 def newfunc(*fargs, **fkeywords):
13 newkeywords = keywords.copy()
14 newkeywords.update(fkeywords)
15 return func(*(args + fargs), **newkeywords)
16 newfunc.func = func
17 newfunc.args = args
18 newfunc.keywords = keywords
19 return newfunc
20
21def capture(*args, **kw):
22 """capture all positional and keyword arguments"""
23 return args, kw
24
Jack Diederiche0cbd692009-04-01 04:27:09 +000025def signature(part):
26 """ return the signature of a partial object """
27 return (part.func, part.args, part.keywords, part.__dict__)
Guido van Rossumc1f779c2007-07-03 08:25:58 +000028
Raymond Hettinger9c323f82005-02-28 19:39:44 +000029class TestPartial(unittest.TestCase):
30
Thomas Wouters4d70c3d2006-06-08 14:42:34 +000031 thetype = functools.partial
Raymond Hettinger9c323f82005-02-28 19:39:44 +000032
33 def test_basic_examples(self):
34 p = self.thetype(capture, 1, 2, a=10, b=20)
35 self.assertEqual(p(3, 4, b=30, c=40),
36 ((1, 2, 3, 4), dict(a=10, b=30, c=40)))
37 p = self.thetype(map, lambda x: x*10)
Guido van Rossumc1f779c2007-07-03 08:25:58 +000038 self.assertEqual(list(p([1,2,3,4])), [10, 20, 30, 40])
Raymond Hettinger9c323f82005-02-28 19:39:44 +000039
40 def test_attributes(self):
41 p = self.thetype(capture, 1, 2, a=10, b=20)
42 # attributes should be readable
43 self.assertEqual(p.func, capture)
44 self.assertEqual(p.args, (1, 2))
45 self.assertEqual(p.keywords, dict(a=10, b=20))
46 # attributes should not be writable
47 if not isinstance(self.thetype, type):
48 return
Georg Brandl89fad142010-03-14 10:23:39 +000049 self.assertRaises(AttributeError, setattr, p, 'func', map)
50 self.assertRaises(AttributeError, setattr, p, 'args', (1, 2))
51 self.assertRaises(AttributeError, setattr, p, 'keywords', dict(a=1, b=2))
52
53 p = self.thetype(hex)
54 try:
55 del p.__dict__
56 except TypeError:
57 pass
58 else:
59 self.fail('partial object allowed __dict__ to be deleted')
Raymond Hettinger9c323f82005-02-28 19:39:44 +000060
61 def test_argument_checking(self):
62 self.assertRaises(TypeError, self.thetype) # need at least a func arg
63 try:
64 self.thetype(2)()
65 except TypeError:
66 pass
67 else:
68 self.fail('First arg not checked for callability')
69
70 def test_protection_of_callers_dict_argument(self):
71 # a caller's dictionary should not be altered by partial
72 def func(a=10, b=20):
73 return a
74 d = {'a':3}
75 p = self.thetype(func, a=5)
76 self.assertEqual(p(**d), 3)
77 self.assertEqual(d, {'a':3})
78 p(b=7)
79 self.assertEqual(d, {'a':3})
80
81 def test_arg_combinations(self):
82 # exercise special code paths for zero args in either partial
83 # object or the caller
84 p = self.thetype(capture)
85 self.assertEqual(p(), ((), {}))
86 self.assertEqual(p(1,2), ((1,2), {}))
87 p = self.thetype(capture, 1, 2)
88 self.assertEqual(p(), ((1,2), {}))
89 self.assertEqual(p(3,4), ((1,2,3,4), {}))
90
91 def test_kw_combinations(self):
92 # exercise special code paths for no keyword args in
93 # either the partial object or the caller
94 p = self.thetype(capture)
95 self.assertEqual(p(), ((), {}))
96 self.assertEqual(p(a=1), ((), {'a':1}))
97 p = self.thetype(capture, a=1)
98 self.assertEqual(p(), ((), {'a':1}))
99 self.assertEqual(p(b=2), ((), {'a':1, 'b':2}))
100 # keyword args in the call override those in the partial object
101 self.assertEqual(p(a=3, b=2), ((), {'a':3, 'b':2}))
102
103 def test_positional(self):
104 # make sure positional arguments are captured correctly
105 for args in [(), (0,), (0,1), (0,1,2), (0,1,2,3)]:
106 p = self.thetype(capture, *args)
107 expected = args + ('x',)
108 got, empty = p('x')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000109 self.assertTrue(expected == got and empty == {})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000110
111 def test_keyword(self):
112 # make sure keyword arguments are captured correctly
113 for a in ['a', 0, None, 3.5]:
114 p = self.thetype(capture, a=a)
115 expected = {'a':a,'x':None}
116 empty, got = p(x=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000117 self.assertTrue(expected == got and empty == ())
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000118
119 def test_no_side_effects(self):
120 # make sure there are no side effects that affect subsequent calls
121 p = self.thetype(capture, 0, a=1)
122 args1, kw1 = p(1, b=2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000123 self.assertTrue(args1 == (0,1) and kw1 == {'a':1,'b':2})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000124 args2, kw2 = p()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000125 self.assertTrue(args2 == (0,) and kw2 == {'a':1})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000126
127 def test_error_propagation(self):
128 def f(x, y):
129 x / y
130 self.assertRaises(ZeroDivisionError, self.thetype(f, 1, 0))
131 self.assertRaises(ZeroDivisionError, self.thetype(f, 1), 0)
132 self.assertRaises(ZeroDivisionError, self.thetype(f), 1, 0)
133 self.assertRaises(ZeroDivisionError, self.thetype(f, y=0), 1)
134
Raymond Hettingerc8b6d1b2005-03-08 06:14:50 +0000135 def test_weakref(self):
136 f = self.thetype(int, base=16)
137 p = proxy(f)
138 self.assertEqual(f.func, p.func)
139 f = None
140 self.assertRaises(ReferenceError, getattr, p, 'func')
141
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000142 def test_with_bound_and_unbound_methods(self):
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000143 data = list(map(str, range(10)))
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000144 join = self.thetype(str.join, '')
145 self.assertEqual(join(data), '0123456789')
146 join = self.thetype(''.join)
147 self.assertEqual(join(data), '0123456789')
Tim Peterseba28be2005-03-28 01:08:02 +0000148
Jack Diederiche0cbd692009-04-01 04:27:09 +0000149 def test_pickle(self):
150 f = self.thetype(signature, 'asdf', bar=True)
151 f.add_something_to__dict__ = True
152 f_copy = pickle.loads(pickle.dumps(f))
153 self.assertEqual(signature(f), signature(f_copy))
154
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000155class PartialSubclass(functools.partial):
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000156 pass
157
158class TestPartialSubclass(TestPartial):
159
160 thetype = PartialSubclass
161
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000162class TestPythonPartial(TestPartial):
163
164 thetype = PythonPartial
165
Jack Diederiche0cbd692009-04-01 04:27:09 +0000166 # the python version isn't picklable
167 def test_pickle(self): pass
168
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000169class TestUpdateWrapper(unittest.TestCase):
170
171 def check_wrapper(self, wrapper, wrapped,
172 assigned=functools.WRAPPER_ASSIGNMENTS,
173 updated=functools.WRAPPER_UPDATES):
174 # Check attributes were assigned
175 for name in assigned:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000176 self.assertTrue(getattr(wrapper, name) is getattr(wrapped, name))
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000177 # Check attributes were updated
178 for name in updated:
179 wrapper_attr = getattr(wrapper, name)
180 wrapped_attr = getattr(wrapped, name)
181 for key in wrapped_attr:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000182 self.assertTrue(wrapped_attr[key] is wrapper_attr[key])
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000183
R. David Murray378c0cf2010-02-24 01:46:21 +0000184 def _default_update(self):
Antoine Pitrou560f7642010-08-04 18:28:02 +0000185 def f(a:'This is a new annotation'):
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000186 """This is a test"""
187 pass
188 f.attr = 'This is also a test'
Antoine Pitrou560f7642010-08-04 18:28:02 +0000189 def wrapper(b:'This is the prior annotation'):
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000190 pass
191 functools.update_wrapper(wrapper, f)
R. David Murray378c0cf2010-02-24 01:46:21 +0000192 return wrapper, f
193
194 def test_default_update(self):
195 wrapper, f = self._default_update()
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000196 self.check_wrapper(wrapper, f)
Nick Coghlan98876832010-08-17 06:17:18 +0000197 self.assertIs(wrapper.__wrapped__, f)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000198 self.assertEqual(wrapper.__name__, 'f')
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000199 self.assertEqual(wrapper.attr, 'This is also a test')
Antoine Pitrou560f7642010-08-04 18:28:02 +0000200 self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation')
201 self.assertNotIn('b', wrapper.__annotations__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000202
R. David Murray378c0cf2010-02-24 01:46:21 +0000203 @unittest.skipIf(sys.flags.optimize >= 2,
204 "Docstrings are omitted with -O2 and above")
205 def test_default_update_doc(self):
206 wrapper, f = self._default_update()
207 self.assertEqual(wrapper.__doc__, 'This is a test')
208
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000209 def test_no_update(self):
210 def f():
211 """This is a test"""
212 pass
213 f.attr = 'This is also a test'
214 def wrapper():
215 pass
216 functools.update_wrapper(wrapper, f, (), ())
217 self.check_wrapper(wrapper, f, (), ())
218 self.assertEqual(wrapper.__name__, 'wrapper')
219 self.assertEqual(wrapper.__doc__, None)
Antoine Pitrou560f7642010-08-04 18:28:02 +0000220 self.assertEqual(wrapper.__annotations__, {})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000221 self.assertFalse(hasattr(wrapper, 'attr'))
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000222
223 def test_selective_update(self):
224 def f():
225 pass
226 f.attr = 'This is a different test'
227 f.dict_attr = dict(a=1, b=2, c=3)
228 def wrapper():
229 pass
230 wrapper.dict_attr = {}
231 assign = ('attr',)
232 update = ('dict_attr',)
233 functools.update_wrapper(wrapper, f, assign, update)
234 self.check_wrapper(wrapper, f, assign, update)
235 self.assertEqual(wrapper.__name__, 'wrapper')
236 self.assertEqual(wrapper.__doc__, None)
237 self.assertEqual(wrapper.attr, 'This is a different test')
238 self.assertEqual(wrapper.dict_attr, f.dict_attr)
239
Nick Coghlan98876832010-08-17 06:17:18 +0000240 def test_missing_attributes(self):
241 def f():
242 pass
243 def wrapper():
244 pass
245 wrapper.dict_attr = {}
246 assign = ('attr',)
247 update = ('dict_attr',)
248 # Missing attributes on wrapped object are ignored
249 functools.update_wrapper(wrapper, f, assign, update)
250 self.assertNotIn('attr', wrapper.__dict__)
251 self.assertEqual(wrapper.dict_attr, {})
252 # Wrapper must have expected attributes for updating
253 del wrapper.dict_attr
254 with self.assertRaises(AttributeError):
255 functools.update_wrapper(wrapper, f, assign, update)
256 wrapper.dict_attr = 1
257 with self.assertRaises(AttributeError):
258 functools.update_wrapper(wrapper, f, assign, update)
259
260 @unittest.skipIf(sys.flags.optimize >= 2,
261 "Docstrings are omitted with -O2 and above")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000262 def test_builtin_update(self):
263 # Test for bug #1576241
264 def wrapper():
265 pass
266 functools.update_wrapper(wrapper, max)
267 self.assertEqual(wrapper.__name__, 'max')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000268 self.assertTrue(wrapper.__doc__.startswith('max('))
Antoine Pitrou560f7642010-08-04 18:28:02 +0000269 self.assertEqual(wrapper.__annotations__, {})
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000270
271class TestWraps(TestUpdateWrapper):
272
R. David Murray378c0cf2010-02-24 01:46:21 +0000273 def _default_update(self):
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000274 def f():
275 """This is a test"""
276 pass
277 f.attr = 'This is also a test'
278 @functools.wraps(f)
279 def wrapper():
280 pass
281 self.check_wrapper(wrapper, f)
R. David Murray378c0cf2010-02-24 01:46:21 +0000282 return wrapper
283
284 def test_default_update(self):
285 wrapper = self._default_update()
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000286 self.assertEqual(wrapper.__name__, 'f')
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000287 self.assertEqual(wrapper.attr, 'This is also a test')
288
R. David Murray378c0cf2010-02-24 01:46:21 +0000289 @unittest.skipIf(not sys.flags.optimize <= 1,
290 "Docstrings are omitted with -O2 and above")
291 def test_default_update_doc(self):
292 wrapper = self._default_update()
293 self.assertEqual(wrapper.__doc__, 'This is a test')
294
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000295 def test_no_update(self):
296 def f():
297 """This is a test"""
298 pass
299 f.attr = 'This is also a test'
300 @functools.wraps(f, (), ())
301 def wrapper():
302 pass
303 self.check_wrapper(wrapper, f, (), ())
304 self.assertEqual(wrapper.__name__, 'wrapper')
305 self.assertEqual(wrapper.__doc__, None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000306 self.assertFalse(hasattr(wrapper, 'attr'))
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000307
308 def test_selective_update(self):
309 def f():
310 pass
311 f.attr = 'This is a different test'
312 f.dict_attr = dict(a=1, b=2, c=3)
313 def add_dict_attr(f):
314 f.dict_attr = {}
315 return f
316 assign = ('attr',)
317 update = ('dict_attr',)
318 @functools.wraps(f, assign, update)
319 @add_dict_attr
320 def wrapper():
321 pass
322 self.check_wrapper(wrapper, f, assign, update)
323 self.assertEqual(wrapper.__name__, 'wrapper')
324 self.assertEqual(wrapper.__doc__, None)
325 self.assertEqual(wrapper.attr, 'This is a different test')
326 self.assertEqual(wrapper.dict_attr, f.dict_attr)
327
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000328class TestReduce(unittest.TestCase):
329 func = functools.reduce
330
331 def test_reduce(self):
332 class Squares:
333 def __init__(self, max):
334 self.max = max
335 self.sofar = []
336
337 def __len__(self):
338 return len(self.sofar)
339
340 def __getitem__(self, i):
341 if not 0 <= i < self.max: raise IndexError
342 n = len(self.sofar)
343 while n <= i:
344 self.sofar.append(n*n)
345 n += 1
346 return self.sofar[i]
Alexander Belopolskye29e6bf2010-08-16 18:55:46 +0000347 def add(x, y):
348 return x + y
349 self.assertEqual(self.func(add, ['a', 'b', 'c'], ''), 'abc')
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000350 self.assertEqual(
Alexander Belopolskye29e6bf2010-08-16 18:55:46 +0000351 self.func(add, [['a', 'c'], [], ['d', 'w']], []),
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000352 ['a','c','d','w']
353 )
354 self.assertEqual(self.func(lambda x, y: x*y, range(2,8), 1), 5040)
355 self.assertEqual(
Guido van Rossume2a383d2007-01-15 16:59:06 +0000356 self.func(lambda x, y: x*y, range(2,21), 1),
357 2432902008176640000
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000358 )
Alexander Belopolskye29e6bf2010-08-16 18:55:46 +0000359 self.assertEqual(self.func(add, Squares(10)), 285)
360 self.assertEqual(self.func(add, Squares(10), 0), 285)
361 self.assertEqual(self.func(add, Squares(0), 0), 0)
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000362 self.assertRaises(TypeError, self.func)
363 self.assertRaises(TypeError, self.func, 42, 42)
364 self.assertRaises(TypeError, self.func, 42, 42, 42)
365 self.assertEqual(self.func(42, "1"), "1") # func is never called with one item
366 self.assertEqual(self.func(42, "", "1"), "1") # func is never called with one item
367 self.assertRaises(TypeError, self.func, 42, (42, 42))
Alexander Belopolskye29e6bf2010-08-16 18:55:46 +0000368 self.assertRaises(TypeError, self.func, add, []) # arg 2 must not be empty sequence with no initial value
369 self.assertRaises(TypeError, self.func, add, "")
370 self.assertRaises(TypeError, self.func, add, ())
371 self.assertRaises(TypeError, self.func, add, object())
372
373 class TestFailingIter:
374 def __iter__(self):
375 raise RuntimeError
376 self.assertRaises(RuntimeError, self.func, add, TestFailingIter())
377
378 self.assertEqual(self.func(add, [], None), None)
379 self.assertEqual(self.func(add, [], 42), 42)
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000380
381 class BadSeq:
382 def __getitem__(self, index):
383 raise ValueError
384 self.assertRaises(ValueError, self.func, 42, BadSeq())
385
386 # Test reduce()'s use of iterators.
387 def test_iterator_usage(self):
388 class SequenceClass:
389 def __init__(self, n):
390 self.n = n
391 def __getitem__(self, i):
392 if 0 <= i < self.n:
393 return i
394 else:
395 raise IndexError
Guido van Rossumd8faa362007-04-27 19:54:29 +0000396
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000397 from operator import add
398 self.assertEqual(self.func(add, SequenceClass(5)), 10)
399 self.assertEqual(self.func(add, SequenceClass(5), 42), 52)
400 self.assertRaises(TypeError, self.func, add, SequenceClass(0))
401 self.assertEqual(self.func(add, SequenceClass(0), 42), 42)
402 self.assertEqual(self.func(add, SequenceClass(1)), 0)
403 self.assertEqual(self.func(add, SequenceClass(1), 42), 42)
404
405 d = {"one": 1, "two": 2, "three": 3}
406 self.assertEqual(self.func(add, d), "".join(d.keys()))
407
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000408class TestCmpToKey(unittest.TestCase):
409 def test_cmp_to_key(self):
410 def mycmp(x, y):
411 return y - x
412 self.assertEqual(sorted(range(5), key=functools.cmp_to_key(mycmp)),
413 [4, 3, 2, 1, 0])
Guido van Rossumd8faa362007-04-27 19:54:29 +0000414
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000415 def test_hash(self):
416 def mycmp(x, y):
417 return y - x
418 key = functools.cmp_to_key(mycmp)
419 k = key(10)
420 self.assertRaises(TypeError, hash(k))
421
422class TestTotalOrdering(unittest.TestCase):
423
424 def test_total_ordering_lt(self):
425 @functools.total_ordering
426 class A:
427 def __init__(self, value):
428 self.value = value
429 def __lt__(self, other):
430 return self.value < other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +0000431 self.assertTrue(A(1) < A(2))
432 self.assertTrue(A(2) > A(1))
433 self.assertTrue(A(1) <= A(2))
434 self.assertTrue(A(2) >= A(1))
435 self.assertTrue(A(2) <= A(2))
436 self.assertTrue(A(2) >= A(2))
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000437
438 def test_total_ordering_le(self):
439 @functools.total_ordering
440 class A:
441 def __init__(self, value):
442 self.value = value
443 def __le__(self, other):
444 return self.value <= other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +0000445 self.assertTrue(A(1) < A(2))
446 self.assertTrue(A(2) > A(1))
447 self.assertTrue(A(1) <= A(2))
448 self.assertTrue(A(2) >= A(1))
449 self.assertTrue(A(2) <= A(2))
450 self.assertTrue(A(2) >= A(2))
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000451
452 def test_total_ordering_gt(self):
453 @functools.total_ordering
454 class A:
455 def __init__(self, value):
456 self.value = value
457 def __gt__(self, other):
458 return self.value > other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +0000459 self.assertTrue(A(1) < A(2))
460 self.assertTrue(A(2) > A(1))
461 self.assertTrue(A(1) <= A(2))
462 self.assertTrue(A(2) >= A(1))
463 self.assertTrue(A(2) <= A(2))
464 self.assertTrue(A(2) >= A(2))
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000465
466 def test_total_ordering_ge(self):
467 @functools.total_ordering
468 class A:
469 def __init__(self, value):
470 self.value = value
471 def __ge__(self, other):
472 return self.value >= other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +0000473 self.assertTrue(A(1) < A(2))
474 self.assertTrue(A(2) > A(1))
475 self.assertTrue(A(1) <= A(2))
476 self.assertTrue(A(2) >= A(1))
477 self.assertTrue(A(2) <= A(2))
478 self.assertTrue(A(2) >= A(2))
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000479
480 def test_total_ordering_no_overwrite(self):
481 # new methods should not overwrite existing
482 @functools.total_ordering
483 class A(int):
Benjamin Peterson9c2930e2010-08-23 17:40:33 +0000484 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +0000485 self.assertTrue(A(1) < A(2))
486 self.assertTrue(A(2) > A(1))
487 self.assertTrue(A(1) <= A(2))
488 self.assertTrue(A(2) >= A(1))
489 self.assertTrue(A(2) <= A(2))
490 self.assertTrue(A(2) >= A(2))
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000491
Benjamin Peterson42ebee32010-04-11 01:43:16 +0000492 def test_no_operations_defined(self):
493 with self.assertRaises(ValueError):
494 @functools.total_ordering
495 class A:
496 pass
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000497
Georg Brandl2e7346a2010-07-31 18:09:23 +0000498class TestLRU(unittest.TestCase):
499
500 def test_lru(self):
501 def orig(x, y):
502 return 3*x+y
503 f = functools.lru_cache(maxsize=20)(orig)
Raymond Hettinger7496b412010-11-30 19:15:45 +0000504 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000505 self.assertEqual(maxsize, 20)
506 self.assertEqual(currsize, 0)
507 self.assertEqual(hits, 0)
508 self.assertEqual(misses, 0)
Georg Brandl2e7346a2010-07-31 18:09:23 +0000509
510 domain = range(5)
511 for i in range(1000):
512 x, y = choice(domain), choice(domain)
513 actual = f(x, y)
514 expected = orig(x, y)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000515 self.assertEqual(actual, expected)
Raymond Hettinger7496b412010-11-30 19:15:45 +0000516 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000517 self.assertTrue(hits > misses)
518 self.assertEqual(hits + misses, 1000)
519 self.assertEqual(currsize, 20)
Georg Brandl2e7346a2010-07-31 18:09:23 +0000520
Raymond Hettinger02566ec2010-09-04 22:46:06 +0000521 f.cache_clear() # test clearing
Raymond Hettinger7496b412010-11-30 19:15:45 +0000522 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000523 self.assertEqual(hits, 0)
524 self.assertEqual(misses, 0)
525 self.assertEqual(currsize, 0)
Georg Brandl2e7346a2010-07-31 18:09:23 +0000526 f(x, y)
Raymond Hettinger7496b412010-11-30 19:15:45 +0000527 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000528 self.assertEqual(hits, 0)
529 self.assertEqual(misses, 1)
530 self.assertEqual(currsize, 1)
Georg Brandl2e7346a2010-07-31 18:09:23 +0000531
Nick Coghlan98876832010-08-17 06:17:18 +0000532 # Test bypassing the cache
533 self.assertIs(f.__wrapped__, orig)
534 f.__wrapped__(x, y)
Raymond Hettinger7496b412010-11-30 19:15:45 +0000535 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000536 self.assertEqual(hits, 0)
537 self.assertEqual(misses, 1)
538 self.assertEqual(currsize, 1)
Nick Coghlan98876832010-08-17 06:17:18 +0000539
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000540 # test size zero (which means "never-cache")
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000541 @functools.lru_cache(0)
542 def f():
543 nonlocal f_cnt
544 f_cnt += 1
545 return 20
Nick Coghlan234515a2010-11-30 06:19:46 +0000546 self.assertEqual(f.cache_info().maxsize, 0)
Raymond Hettingerf3098282010-08-15 03:30:45 +0000547 f_cnt = 0
548 for i in range(5):
549 self.assertEqual(f(), 20)
550 self.assertEqual(f_cnt, 5)
Raymond Hettinger7496b412010-11-30 19:15:45 +0000551 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000552 self.assertEqual(hits, 0)
553 self.assertEqual(misses, 5)
554 self.assertEqual(currsize, 0)
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000555
556 # test size one
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000557 @functools.lru_cache(1)
558 def f():
559 nonlocal f_cnt
560 f_cnt += 1
561 return 20
Nick Coghlan234515a2010-11-30 06:19:46 +0000562 self.assertEqual(f.cache_info().maxsize, 1)
Raymond Hettingerf3098282010-08-15 03:30:45 +0000563 f_cnt = 0
564 for i in range(5):
565 self.assertEqual(f(), 20)
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000566 self.assertEqual(f_cnt, 1)
Raymond Hettinger7496b412010-11-30 19:15:45 +0000567 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000568 self.assertEqual(hits, 4)
569 self.assertEqual(misses, 1)
570 self.assertEqual(currsize, 1)
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000571
Raymond Hettingerf3098282010-08-15 03:30:45 +0000572 # test size two
573 @functools.lru_cache(2)
574 def f(x):
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000575 nonlocal f_cnt
576 f_cnt += 1
Raymond Hettingerf3098282010-08-15 03:30:45 +0000577 return x*10
Nick Coghlan234515a2010-11-30 06:19:46 +0000578 self.assertEqual(f.cache_info().maxsize, 2)
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000579 f_cnt = 0
Raymond Hettingerf3098282010-08-15 03:30:45 +0000580 for x in 7, 9, 7, 9, 7, 9, 8, 8, 8, 9, 9, 9, 8, 8, 8, 7:
581 # * * * *
582 self.assertEqual(f(x), x*10)
583 self.assertEqual(f_cnt, 4)
Raymond Hettinger7496b412010-11-30 19:15:45 +0000584 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +0000585 self.assertEqual(hits, 12)
586 self.assertEqual(misses, 4)
587 self.assertEqual(currsize, 2)
Raymond Hettinger0f56e902010-08-14 23:52:08 +0000588
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000589def test_main(verbose=None):
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000590 test_classes = (
591 TestPartial,
592 TestPartialSubclass,
593 TestPythonPartial,
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000594 TestUpdateWrapper,
Benjamin Peterson9c2930e2010-08-23 17:40:33 +0000595 TestTotalOrdering,
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000596 TestWraps,
Georg Brandl2e7346a2010-07-31 18:09:23 +0000597 TestReduce,
598 TestLRU,
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000599 )
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000600 support.run_unittest(*test_classes)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000601
602 # verify reference counting
603 if verbose and hasattr(sys, "gettotalrefcount"):
604 import gc
605 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000606 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000607 support.run_unittest(*test_classes)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000608 gc.collect()
609 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000610 print(counts)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000611
612if __name__ == '__main__':
613 test_main(verbose=True)