blob: 9503f4086b1cb9f670ac6fb7e22963749bd8ba0c [file] [log] [blame]
Nick Coghlanf4cb48a2013-11-03 16:41:46 +10001import abc
Raymond Hettingeraf56e0e2016-12-16 13:57:40 -08002import builtins
Raymond Hettinger003be522011-05-03 11:01:32 -07003import collections
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03004import collections.abc
Serhiy Storchaka45120f22015-10-24 09:49:56 +03005import copy
Pablo Galindo99e6c262020-01-23 15:29:52 +00006from itertools import permutations, chain
Jack Diederiche0cbd692009-04-01 04:27:09 +00007import pickle
Georg Brandl2e7346a2010-07-31 18:09:23 +00008from random import choice
Łukasz Langa6f692512013-06-05 12:20:24 +02009import sys
10from test import support
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020011import threading
Serhiy Storchaka67796522017-01-12 18:34:33 +020012import time
Łukasz Langae5697532017-12-11 13:56:31 -080013import typing
Łukasz Langa6f692512013-06-05 12:20:24 +020014import unittest
Raymond Hettingerd191ef22017-01-07 20:44:48 -080015import unittest.mock
Pablo Galindo99e6c262020-01-23 15:29:52 +000016import os
Łukasz Langa6f692512013-06-05 12:20:24 +020017from weakref import proxy
Nick Coghlan457fc9a2016-09-10 20:00:02 +100018import contextlib
Raymond Hettinger9c323f82005-02-28 19:39:44 +000019
Pablo Galindo99e6c262020-01-23 15:29:52 +000020from test.support.script_helper import assert_python_ok
21
Antoine Pitroub5b37142012-11-13 21:35:40 +010022import functools
23
Antoine Pitroub5b37142012-11-13 21:35:40 +010024py_functools = support.import_fresh_module('functools', blocked=['_functools'])
25c_functools = support.import_fresh_module('functools', fresh=['_functools'])
26
Łukasz Langa6f692512013-06-05 12:20:24 +020027decimal = support.import_fresh_module('decimal', fresh=['_decimal'])
28
Nick Coghlan457fc9a2016-09-10 20:00:02 +100029@contextlib.contextmanager
30def replaced_module(name, replacement):
31 original_module = sys.modules[name]
32 sys.modules[name] = replacement
33 try:
34 yield
35 finally:
36 sys.modules[name] = original_module
Łukasz Langa6f692512013-06-05 12:20:24 +020037
Raymond Hettinger9c323f82005-02-28 19:39:44 +000038def capture(*args, **kw):
39 """capture all positional and keyword arguments"""
40 return args, kw
41
Łukasz Langa6f692512013-06-05 12:20:24 +020042
Jack Diederiche0cbd692009-04-01 04:27:09 +000043def signature(part):
44 """ return the signature of a partial object """
45 return (part.func, part.args, part.keywords, part.__dict__)
Guido van Rossumc1f779c2007-07-03 08:25:58 +000046
Serhiy Storchaka38741282016-02-02 18:45:17 +020047class MyTuple(tuple):
48 pass
49
50class BadTuple(tuple):
51 def __add__(self, other):
52 return list(self) + list(other)
53
54class MyDict(dict):
55 pass
56
Łukasz Langa6f692512013-06-05 12:20:24 +020057
Serhiy Storchakaca4220b2013-02-05 22:12:59 +020058class TestPartial:
Raymond Hettinger9c323f82005-02-28 19:39:44 +000059
60 def test_basic_examples(self):
Antoine Pitroub5b37142012-11-13 21:35:40 +010061 p = self.partial(capture, 1, 2, a=10, b=20)
62 self.assertTrue(callable(p))
Raymond Hettinger9c323f82005-02-28 19:39:44 +000063 self.assertEqual(p(3, 4, b=30, c=40),
64 ((1, 2, 3, 4), dict(a=10, b=30, c=40)))
Antoine Pitroub5b37142012-11-13 21:35:40 +010065 p = self.partial(map, lambda x: x*10)
Guido van Rossumc1f779c2007-07-03 08:25:58 +000066 self.assertEqual(list(p([1,2,3,4])), [10, 20, 30, 40])
Raymond Hettinger9c323f82005-02-28 19:39:44 +000067
68 def test_attributes(self):
Antoine Pitroub5b37142012-11-13 21:35:40 +010069 p = self.partial(capture, 1, 2, a=10, b=20)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000070 # attributes should be readable
71 self.assertEqual(p.func, capture)
72 self.assertEqual(p.args, (1, 2))
73 self.assertEqual(p.keywords, dict(a=10, b=20))
Raymond Hettinger9c323f82005-02-28 19:39:44 +000074
75 def test_argument_checking(self):
Antoine Pitroub5b37142012-11-13 21:35:40 +010076 self.assertRaises(TypeError, self.partial) # need at least a func arg
Raymond Hettinger9c323f82005-02-28 19:39:44 +000077 try:
Antoine Pitroub5b37142012-11-13 21:35:40 +010078 self.partial(2)()
Raymond Hettinger9c323f82005-02-28 19:39:44 +000079 except TypeError:
80 pass
81 else:
82 self.fail('First arg not checked for callability')
83
84 def test_protection_of_callers_dict_argument(self):
85 # a caller's dictionary should not be altered by partial
86 def func(a=10, b=20):
87 return a
88 d = {'a':3}
Antoine Pitroub5b37142012-11-13 21:35:40 +010089 p = self.partial(func, a=5)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000090 self.assertEqual(p(**d), 3)
91 self.assertEqual(d, {'a':3})
92 p(b=7)
93 self.assertEqual(d, {'a':3})
94
Serhiy Storchaka9639e4a2017-02-20 14:04:30 +020095 def test_kwargs_copy(self):
96 # Issue #29532: Altering a kwarg dictionary passed to a constructor
97 # should not affect a partial object after creation
98 d = {'a': 3}
99 p = self.partial(capture, **d)
100 self.assertEqual(p(), ((), {'a': 3}))
101 d['a'] = 5
102 self.assertEqual(p(), ((), {'a': 3}))
103
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000104 def test_arg_combinations(self):
105 # exercise special code paths for zero args in either partial
106 # object or the caller
Antoine Pitroub5b37142012-11-13 21:35:40 +0100107 p = self.partial(capture)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000108 self.assertEqual(p(), ((), {}))
109 self.assertEqual(p(1,2), ((1,2), {}))
Antoine Pitroub5b37142012-11-13 21:35:40 +0100110 p = self.partial(capture, 1, 2)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000111 self.assertEqual(p(), ((1,2), {}))
112 self.assertEqual(p(3,4), ((1,2,3,4), {}))
113
114 def test_kw_combinations(self):
115 # exercise special code paths for no keyword args in
116 # either the partial object or the caller
Antoine Pitroub5b37142012-11-13 21:35:40 +0100117 p = self.partial(capture)
Benjamin Peterson65bcdd72015-05-09 00:25:18 -0400118 self.assertEqual(p.keywords, {})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000119 self.assertEqual(p(), ((), {}))
120 self.assertEqual(p(a=1), ((), {'a':1}))
Antoine Pitroub5b37142012-11-13 21:35:40 +0100121 p = self.partial(capture, a=1)
Benjamin Peterson65bcdd72015-05-09 00:25:18 -0400122 self.assertEqual(p.keywords, {'a':1})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000123 self.assertEqual(p(), ((), {'a':1}))
124 self.assertEqual(p(b=2), ((), {'a':1, 'b':2}))
125 # keyword args in the call override those in the partial object
126 self.assertEqual(p(a=3, b=2), ((), {'a':3, 'b':2}))
127
128 def test_positional(self):
129 # make sure positional arguments are captured correctly
130 for args in [(), (0,), (0,1), (0,1,2), (0,1,2,3)]:
Antoine Pitroub5b37142012-11-13 21:35:40 +0100131 p = self.partial(capture, *args)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000132 expected = args + ('x',)
133 got, empty = p('x')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000134 self.assertTrue(expected == got and empty == {})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000135
136 def test_keyword(self):
137 # make sure keyword arguments are captured correctly
138 for a in ['a', 0, None, 3.5]:
Antoine Pitroub5b37142012-11-13 21:35:40 +0100139 p = self.partial(capture, a=a)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000140 expected = {'a':a,'x':None}
141 empty, got = p(x=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000142 self.assertTrue(expected == got and empty == ())
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000143
144 def test_no_side_effects(self):
145 # make sure there are no side effects that affect subsequent calls
Antoine Pitroub5b37142012-11-13 21:35:40 +0100146 p = self.partial(capture, 0, a=1)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000147 args1, kw1 = p(1, b=2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000148 self.assertTrue(args1 == (0,1) and kw1 == {'a':1,'b':2})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000149 args2, kw2 = p()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000150 self.assertTrue(args2 == (0,) and kw2 == {'a':1})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000151
152 def test_error_propagation(self):
153 def f(x, y):
154 x / y
Antoine Pitroub5b37142012-11-13 21:35:40 +0100155 self.assertRaises(ZeroDivisionError, self.partial(f, 1, 0))
156 self.assertRaises(ZeroDivisionError, self.partial(f, 1), 0)
157 self.assertRaises(ZeroDivisionError, self.partial(f), 1, 0)
158 self.assertRaises(ZeroDivisionError, self.partial(f, y=0), 1)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000159
Raymond Hettingerc8b6d1b2005-03-08 06:14:50 +0000160 def test_weakref(self):
Antoine Pitroub5b37142012-11-13 21:35:40 +0100161 f = self.partial(int, base=16)
Raymond Hettingerc8b6d1b2005-03-08 06:14:50 +0000162 p = proxy(f)
163 self.assertEqual(f.func, p.func)
164 f = None
165 self.assertRaises(ReferenceError, getattr, p, 'func')
166
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000167 def test_with_bound_and_unbound_methods(self):
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000168 data = list(map(str, range(10)))
Antoine Pitroub5b37142012-11-13 21:35:40 +0100169 join = self.partial(str.join, '')
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000170 self.assertEqual(join(data), '0123456789')
Antoine Pitroub5b37142012-11-13 21:35:40 +0100171 join = self.partial(''.join)
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000172 self.assertEqual(join(data), '0123456789')
Tim Peterseba28be2005-03-28 01:08:02 +0000173
Alexander Belopolskye49af342015-03-01 15:08:17 -0500174 def test_nested_optimization(self):
175 partial = self.partial
Alexander Belopolskye49af342015-03-01 15:08:17 -0500176 inner = partial(signature, 'asdf')
177 nested = partial(inner, bar=True)
178 flat = partial(signature, 'asdf', bar=True)
179 self.assertEqual(signature(nested), signature(flat))
180
Berker Peksag9b93c6b2015-09-22 13:08:16 +0300181 def test_nested_partial_with_attribute(self):
182 # see issue 25137
183 partial = self.partial
184
185 def foo(bar):
186 return bar
187
188 p = partial(foo, 'first')
189 p2 = partial(p, 'second')
190 p2.new_attr = 'spam'
191 self.assertEqual(p2.new_attr, 'spam')
192
Alexander Belopolsky41e422a2010-12-01 20:05:49 +0000193 def test_repr(self):
194 args = (object(), object())
195 args_repr = ', '.join(repr(a) for a in args)
Serhiy Storchaka0aa74e12015-02-15 16:20:47 +0200196 kwargs = {'a': object(), 'b': object()}
197 kwargs_reprs = ['a={a!r}, b={b!r}'.format_map(kwargs),
198 'b={b!r}, a={a!r}'.format_map(kwargs)]
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000199 if self.partial in (c_functools.partial, py_functools.partial):
Alexander Belopolsky41e422a2010-12-01 20:05:49 +0000200 name = 'functools.partial'
201 else:
Antoine Pitroub5b37142012-11-13 21:35:40 +0100202 name = self.partial.__name__
Alexander Belopolsky41e422a2010-12-01 20:05:49 +0000203
Antoine Pitroub5b37142012-11-13 21:35:40 +0100204 f = self.partial(capture)
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000205 self.assertEqual(f'{name}({capture!r})', repr(f))
Alexander Belopolsky41e422a2010-12-01 20:05:49 +0000206
Antoine Pitroub5b37142012-11-13 21:35:40 +0100207 f = self.partial(capture, *args)
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000208 self.assertEqual(f'{name}({capture!r}, {args_repr})', repr(f))
Alexander Belopolsky41e422a2010-12-01 20:05:49 +0000209
Antoine Pitroub5b37142012-11-13 21:35:40 +0100210 f = self.partial(capture, **kwargs)
Serhiy Storchaka0aa74e12015-02-15 16:20:47 +0200211 self.assertIn(repr(f),
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000212 [f'{name}({capture!r}, {kwargs_repr})'
Serhiy Storchaka0aa74e12015-02-15 16:20:47 +0200213 for kwargs_repr in kwargs_reprs])
Alexander Belopolsky41e422a2010-12-01 20:05:49 +0000214
Antoine Pitroub5b37142012-11-13 21:35:40 +0100215 f = self.partial(capture, *args, **kwargs)
Serhiy Storchaka0aa74e12015-02-15 16:20:47 +0200216 self.assertIn(repr(f),
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000217 [f'{name}({capture!r}, {args_repr}, {kwargs_repr})'
Serhiy Storchaka0aa74e12015-02-15 16:20:47 +0200218 for kwargs_repr in kwargs_reprs])
Alexander Belopolsky41e422a2010-12-01 20:05:49 +0000219
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300220 def test_recursive_repr(self):
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000221 if self.partial in (c_functools.partial, py_functools.partial):
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300222 name = 'functools.partial'
223 else:
224 name = self.partial.__name__
225
226 f = self.partial(capture)
227 f.__setstate__((f, (), {}, {}))
Serhiy Storchaka46fe29d2016-06-12 15:45:14 +0300228 try:
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000229 self.assertEqual(repr(f), '%s(...)' % (name,))
Serhiy Storchaka46fe29d2016-06-12 15:45:14 +0300230 finally:
231 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300232
233 f = self.partial(capture)
234 f.__setstate__((capture, (f,), {}, {}))
Serhiy Storchaka46fe29d2016-06-12 15:45:14 +0300235 try:
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000236 self.assertEqual(repr(f), '%s(%r, ...)' % (name, capture,))
Serhiy Storchaka46fe29d2016-06-12 15:45:14 +0300237 finally:
238 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300239
240 f = self.partial(capture)
241 f.__setstate__((capture, (), {'a': f}, {}))
Serhiy Storchaka46fe29d2016-06-12 15:45:14 +0300242 try:
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000243 self.assertEqual(repr(f), '%s(%r, a=...)' % (name, capture,))
Serhiy Storchaka46fe29d2016-06-12 15:45:14 +0300244 finally:
245 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300246
Jack Diederiche0cbd692009-04-01 04:27:09 +0000247 def test_pickle(self):
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000248 with self.AllowPickle():
249 f = self.partial(signature, ['asdf'], bar=[True])
250 f.attr = []
251 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
252 f_copy = pickle.loads(pickle.dumps(f, proto))
253 self.assertEqual(signature(f_copy), signature(f))
Serhiy Storchaka38741282016-02-02 18:45:17 +0200254
255 def test_copy(self):
256 f = self.partial(signature, ['asdf'], bar=[True])
257 f.attr = []
258 f_copy = copy.copy(f)
259 self.assertEqual(signature(f_copy), signature(f))
260 self.assertIs(f_copy.attr, f.attr)
261 self.assertIs(f_copy.args, f.args)
262 self.assertIs(f_copy.keywords, f.keywords)
263
264 def test_deepcopy(self):
265 f = self.partial(signature, ['asdf'], bar=[True])
266 f.attr = []
267 f_copy = copy.deepcopy(f)
268 self.assertEqual(signature(f_copy), signature(f))
269 self.assertIsNot(f_copy.attr, f.attr)
270 self.assertIsNot(f_copy.args, f.args)
271 self.assertIsNot(f_copy.args[0], f.args[0])
272 self.assertIsNot(f_copy.keywords, f.keywords)
273 self.assertIsNot(f_copy.keywords['bar'], f.keywords['bar'])
274
275 def test_setstate(self):
276 f = self.partial(signature)
277 f.__setstate__((capture, (1,), dict(a=10), dict(attr=[])))
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000278
Serhiy Storchaka38741282016-02-02 18:45:17 +0200279 self.assertEqual(signature(f),
280 (capture, (1,), dict(a=10), dict(attr=[])))
281 self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20}))
282
283 f.__setstate__((capture, (1,), dict(a=10), None))
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000284
Serhiy Storchaka38741282016-02-02 18:45:17 +0200285 self.assertEqual(signature(f), (capture, (1,), dict(a=10), {}))
286 self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20}))
287
288 f.__setstate__((capture, (1,), None, None))
289 #self.assertEqual(signature(f), (capture, (1,), {}, {}))
290 self.assertEqual(f(2, b=20), ((1, 2), {'b': 20}))
291 self.assertEqual(f(2), ((1, 2), {}))
292 self.assertEqual(f(), ((1,), {}))
293
294 f.__setstate__((capture, (), {}, None))
295 self.assertEqual(signature(f), (capture, (), {}, {}))
296 self.assertEqual(f(2, b=20), ((2,), {'b': 20}))
297 self.assertEqual(f(2), ((2,), {}))
298 self.assertEqual(f(), ((), {}))
299
300 def test_setstate_errors(self):
301 f = self.partial(signature)
302 self.assertRaises(TypeError, f.__setstate__, (capture, (), {}))
303 self.assertRaises(TypeError, f.__setstate__, (capture, (), {}, {}, None))
304 self.assertRaises(TypeError, f.__setstate__, [capture, (), {}, None])
305 self.assertRaises(TypeError, f.__setstate__, (None, (), {}, None))
306 self.assertRaises(TypeError, f.__setstate__, (capture, None, {}, None))
307 self.assertRaises(TypeError, f.__setstate__, (capture, [], {}, None))
308 self.assertRaises(TypeError, f.__setstate__, (capture, (), [], None))
309
310 def test_setstate_subclasses(self):
311 f = self.partial(signature)
312 f.__setstate__((capture, MyTuple((1,)), MyDict(a=10), None))
313 s = signature(f)
314 self.assertEqual(s, (capture, (1,), dict(a=10), {}))
315 self.assertIs(type(s[1]), tuple)
316 self.assertIs(type(s[2]), dict)
317 r = f()
318 self.assertEqual(r, ((1,), {'a': 10}))
319 self.assertIs(type(r[0]), tuple)
320 self.assertIs(type(r[1]), dict)
321
322 f.__setstate__((capture, BadTuple((1,)), {}, None))
323 s = signature(f)
324 self.assertEqual(s, (capture, (1,), {}, {}))
325 self.assertIs(type(s[1]), tuple)
326 r = f(2)
327 self.assertEqual(r, ((1, 2), {}))
328 self.assertIs(type(r[0]), tuple)
Jack Diederiche0cbd692009-04-01 04:27:09 +0000329
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300330 def test_recursive_pickle(self):
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000331 with self.AllowPickle():
332 f = self.partial(capture)
333 f.__setstate__((f, (), {}, {}))
334 try:
335 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
336 with self.assertRaises(RecursionError):
337 pickle.dumps(f, proto)
338 finally:
339 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300340
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000341 f = self.partial(capture)
342 f.__setstate__((capture, (f,), {}, {}))
343 try:
344 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
345 f_copy = pickle.loads(pickle.dumps(f, proto))
346 try:
347 self.assertIs(f_copy.args[0], f_copy)
348 finally:
349 f_copy.__setstate__((capture, (), {}, {}))
350 finally:
351 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300352
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000353 f = self.partial(capture)
354 f.__setstate__((capture, (), {'a': f}, {}))
355 try:
356 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
357 f_copy = pickle.loads(pickle.dumps(f, proto))
358 try:
359 self.assertIs(f_copy.keywords['a'], f_copy)
360 finally:
361 f_copy.__setstate__((capture, (), {}, {}))
362 finally:
363 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka179f9602016-06-12 11:44:06 +0300364
Serhiy Storchaka19c4e0d2013-02-04 12:47:24 +0200365 # Issue 6083: Reference counting bug
366 def test_setstate_refcount(self):
367 class BadSequence:
368 def __len__(self):
369 return 4
370 def __getitem__(self, key):
371 if key == 0:
372 return max
373 elif key == 1:
374 return tuple(range(1000000))
375 elif key in (2, 3):
376 return {}
377 raise IndexError
378
Serhiy Storchakab6a53402013-02-04 12:57:16 +0200379 f = self.partial(object)
Serhiy Storchaka38741282016-02-02 18:45:17 +0200380 self.assertRaises(TypeError, f.__setstate__, BadSequence())
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000381
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000382@unittest.skipUnless(c_functools, 'requires the C _functools module')
383class TestPartialC(TestPartial, unittest.TestCase):
384 if c_functools:
385 partial = c_functools.partial
386
387 class AllowPickle:
388 def __enter__(self):
389 return self
390 def __exit__(self, type, value, tb):
391 return False
392
393 def test_attributes_unwritable(self):
394 # attributes should not be writable
395 p = self.partial(capture, 1, 2, a=10, b=20)
396 self.assertRaises(AttributeError, setattr, p, 'func', map)
397 self.assertRaises(AttributeError, setattr, p, 'args', (1, 2))
398 self.assertRaises(AttributeError, setattr, p, 'keywords', dict(a=1, b=2))
399
400 p = self.partial(hex)
401 try:
402 del p.__dict__
403 except TypeError:
404 pass
405 else:
406 self.fail('partial object allowed __dict__ to be deleted')
Łukasz Langa6f692512013-06-05 12:20:24 +0200407
Michael Seifert6c3d5272017-03-15 06:26:33 +0100408 def test_manually_adding_non_string_keyword(self):
409 p = self.partial(capture)
410 # Adding a non-string/unicode keyword to partial kwargs
411 p.keywords[1234] = 'value'
412 r = repr(p)
413 self.assertIn('1234', r)
414 self.assertIn("'value'", r)
415 with self.assertRaises(TypeError):
416 p()
417
418 def test_keystr_replaces_value(self):
419 p = self.partial(capture)
420
421 class MutatesYourDict(object):
422 def __str__(self):
423 p.keywords[self] = ['sth2']
424 return 'astr'
425
Mike53f7a7c2017-12-14 14:04:53 +0300426 # Replacing the value during key formatting should keep the original
Michael Seifert6c3d5272017-03-15 06:26:33 +0100427 # value alive (at least long enough).
428 p.keywords[MutatesYourDict()] = ['sth']
429 r = repr(p)
430 self.assertIn('astr', r)
431 self.assertIn("['sth']", r)
432
433
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200434class TestPartialPy(TestPartial, unittest.TestCase):
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000435 partial = py_functools.partial
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000436
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000437 class AllowPickle:
438 def __init__(self):
439 self._cm = replaced_module("functools", py_functools)
440 def __enter__(self):
441 return self._cm.__enter__()
442 def __exit__(self, type, value, tb):
443 return self._cm.__exit__(type, value, tb)
Łukasz Langa6f692512013-06-05 12:20:24 +0200444
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200445if c_functools:
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000446 class CPartialSubclass(c_functools.partial):
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200447 pass
Antoine Pitrou33543272012-11-13 21:36:21 +0100448
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000449class PyPartialSubclass(py_functools.partial):
450 pass
Łukasz Langa6f692512013-06-05 12:20:24 +0200451
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200452@unittest.skipUnless(c_functools, 'requires the C _functools module')
Serhiy Storchakab6a53402013-02-04 12:57:16 +0200453class TestPartialCSubclass(TestPartialC):
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200454 if c_functools:
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000455 partial = CPartialSubclass
Jack Diederiche0cbd692009-04-01 04:27:09 +0000456
Berker Peksag9b93c6b2015-09-22 13:08:16 +0300457 # partial subclasses are not optimized for nested calls
458 test_nested_optimization = None
459
Nick Coghlan457fc9a2016-09-10 20:00:02 +1000460class TestPartialPySubclass(TestPartialPy):
461 partial = PyPartialSubclass
Łukasz Langa6f692512013-06-05 12:20:24 +0200462
Nick Coghlanf4cb48a2013-11-03 16:41:46 +1000463class TestPartialMethod(unittest.TestCase):
464
465 class A(object):
466 nothing = functools.partialmethod(capture)
467 positional = functools.partialmethod(capture, 1)
468 keywords = functools.partialmethod(capture, a=2)
469 both = functools.partialmethod(capture, 3, b=4)
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300470 spec_keywords = functools.partialmethod(capture, self=1, func=2)
Nick Coghlanf4cb48a2013-11-03 16:41:46 +1000471
472 nested = functools.partialmethod(positional, 5)
473
474 over_partial = functools.partialmethod(functools.partial(capture, c=6), 7)
475
476 static = functools.partialmethod(staticmethod(capture), 8)
477 cls = functools.partialmethod(classmethod(capture), d=9)
478
479 a = A()
480
481 def test_arg_combinations(self):
482 self.assertEqual(self.a.nothing(), ((self.a,), {}))
483 self.assertEqual(self.a.nothing(5), ((self.a, 5), {}))
484 self.assertEqual(self.a.nothing(c=6), ((self.a,), {'c': 6}))
485 self.assertEqual(self.a.nothing(5, c=6), ((self.a, 5), {'c': 6}))
486
487 self.assertEqual(self.a.positional(), ((self.a, 1), {}))
488 self.assertEqual(self.a.positional(5), ((self.a, 1, 5), {}))
489 self.assertEqual(self.a.positional(c=6), ((self.a, 1), {'c': 6}))
490 self.assertEqual(self.a.positional(5, c=6), ((self.a, 1, 5), {'c': 6}))
491
492 self.assertEqual(self.a.keywords(), ((self.a,), {'a': 2}))
493 self.assertEqual(self.a.keywords(5), ((self.a, 5), {'a': 2}))
494 self.assertEqual(self.a.keywords(c=6), ((self.a,), {'a': 2, 'c': 6}))
495 self.assertEqual(self.a.keywords(5, c=6), ((self.a, 5), {'a': 2, 'c': 6}))
496
497 self.assertEqual(self.a.both(), ((self.a, 3), {'b': 4}))
498 self.assertEqual(self.a.both(5), ((self.a, 3, 5), {'b': 4}))
499 self.assertEqual(self.a.both(c=6), ((self.a, 3), {'b': 4, 'c': 6}))
500 self.assertEqual(self.a.both(5, c=6), ((self.a, 3, 5), {'b': 4, 'c': 6}))
501
502 self.assertEqual(self.A.both(self.a, 5, c=6), ((self.a, 3, 5), {'b': 4, 'c': 6}))
503
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300504 self.assertEqual(self.a.spec_keywords(), ((self.a,), {'self': 1, 'func': 2}))
505
Nick Coghlanf4cb48a2013-11-03 16:41:46 +1000506 def test_nested(self):
507 self.assertEqual(self.a.nested(), ((self.a, 1, 5), {}))
508 self.assertEqual(self.a.nested(6), ((self.a, 1, 5, 6), {}))
509 self.assertEqual(self.a.nested(d=7), ((self.a, 1, 5), {'d': 7}))
510 self.assertEqual(self.a.nested(6, d=7), ((self.a, 1, 5, 6), {'d': 7}))
511
512 self.assertEqual(self.A.nested(self.a, 6, d=7), ((self.a, 1, 5, 6), {'d': 7}))
513
514 def test_over_partial(self):
515 self.assertEqual(self.a.over_partial(), ((self.a, 7), {'c': 6}))
516 self.assertEqual(self.a.over_partial(5), ((self.a, 7, 5), {'c': 6}))
517 self.assertEqual(self.a.over_partial(d=8), ((self.a, 7), {'c': 6, 'd': 8}))
518 self.assertEqual(self.a.over_partial(5, d=8), ((self.a, 7, 5), {'c': 6, 'd': 8}))
519
520 self.assertEqual(self.A.over_partial(self.a, 5, d=8), ((self.a, 7, 5), {'c': 6, 'd': 8}))
521
522 def test_bound_method_introspection(self):
523 obj = self.a
524 self.assertIs(obj.both.__self__, obj)
525 self.assertIs(obj.nested.__self__, obj)
526 self.assertIs(obj.over_partial.__self__, obj)
527 self.assertIs(obj.cls.__self__, self.A)
528 self.assertIs(self.A.cls.__self__, self.A)
529
530 def test_unbound_method_retrieval(self):
531 obj = self.A
532 self.assertFalse(hasattr(obj.both, "__self__"))
533 self.assertFalse(hasattr(obj.nested, "__self__"))
534 self.assertFalse(hasattr(obj.over_partial, "__self__"))
535 self.assertFalse(hasattr(obj.static, "__self__"))
536 self.assertFalse(hasattr(self.a.static, "__self__"))
537
538 def test_descriptors(self):
539 for obj in [self.A, self.a]:
540 with self.subTest(obj=obj):
541 self.assertEqual(obj.static(), ((8,), {}))
542 self.assertEqual(obj.static(5), ((8, 5), {}))
543 self.assertEqual(obj.static(d=8), ((8,), {'d': 8}))
544 self.assertEqual(obj.static(5, d=8), ((8, 5), {'d': 8}))
545
546 self.assertEqual(obj.cls(), ((self.A,), {'d': 9}))
547 self.assertEqual(obj.cls(5), ((self.A, 5), {'d': 9}))
548 self.assertEqual(obj.cls(c=8), ((self.A,), {'c': 8, 'd': 9}))
549 self.assertEqual(obj.cls(5, c=8), ((self.A, 5), {'c': 8, 'd': 9}))
550
551 def test_overriding_keywords(self):
552 self.assertEqual(self.a.keywords(a=3), ((self.a,), {'a': 3}))
553 self.assertEqual(self.A.keywords(self.a, a=3), ((self.a,), {'a': 3}))
554
555 def test_invalid_args(self):
556 with self.assertRaises(TypeError):
557 class B(object):
558 method = functools.partialmethod(None, 1)
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300559 with self.assertRaises(TypeError):
560 class B:
561 method = functools.partialmethod()
Serhiy Storchaka142566c2019-06-05 18:22:31 +0300562 with self.assertRaises(TypeError):
Serhiy Storchaka42a139e2019-04-01 09:16:35 +0300563 class B:
564 method = functools.partialmethod(func=capture, a=1)
Nick Coghlanf4cb48a2013-11-03 16:41:46 +1000565
566 def test_repr(self):
567 self.assertEqual(repr(vars(self.A)['both']),
568 'functools.partialmethod({}, 3, b=4)'.format(capture))
569
570 def test_abstract(self):
571 class Abstract(abc.ABCMeta):
572
573 @abc.abstractmethod
574 def add(self, x, y):
575 pass
576
577 add5 = functools.partialmethod(add, 5)
578
579 self.assertTrue(Abstract.add.__isabstractmethod__)
580 self.assertTrue(Abstract.add5.__isabstractmethod__)
581
582 for func in [self.A.static, self.A.cls, self.A.over_partial, self.A.nested, self.A.both]:
583 self.assertFalse(getattr(func, '__isabstractmethod__', False))
584
Pablo Galindo8c77b8c2019-04-29 13:36:57 +0100585 def test_positional_only(self):
586 def f(a, b, /):
587 return a + b
588
589 p = functools.partial(f, 1)
590 self.assertEqual(p(2), f(1, 2))
591
Nick Coghlanf4cb48a2013-11-03 16:41:46 +1000592
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000593class TestUpdateWrapper(unittest.TestCase):
594
595 def check_wrapper(self, wrapper, wrapped,
596 assigned=functools.WRAPPER_ASSIGNMENTS,
597 updated=functools.WRAPPER_UPDATES):
598 # Check attributes were assigned
599 for name in assigned:
Nick Coghlan24c05bc2013-07-15 21:13:08 +1000600 self.assertIs(getattr(wrapper, name), getattr(wrapped, name))
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000601 # Check attributes were updated
602 for name in updated:
603 wrapper_attr = getattr(wrapper, name)
604 wrapped_attr = getattr(wrapped, name)
605 for key in wrapped_attr:
Nick Coghlan24c05bc2013-07-15 21:13:08 +1000606 if name == "__dict__" and key == "__wrapped__":
607 # __wrapped__ is overwritten by the update code
608 continue
609 self.assertIs(wrapped_attr[key], wrapper_attr[key])
610 # Check __wrapped__
611 self.assertIs(wrapper.__wrapped__, wrapped)
612
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000613
R. David Murray378c0cf2010-02-24 01:46:21 +0000614 def _default_update(self):
Antoine Pitrou560f7642010-08-04 18:28:02 +0000615 def f(a:'This is a new annotation'):
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000616 """This is a test"""
617 pass
618 f.attr = 'This is also a test'
Nick Coghlan24c05bc2013-07-15 21:13:08 +1000619 f.__wrapped__ = "This is a bald faced lie"
Antoine Pitrou560f7642010-08-04 18:28:02 +0000620 def wrapper(b:'This is the prior annotation'):
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000621 pass
622 functools.update_wrapper(wrapper, f)
R. David Murray378c0cf2010-02-24 01:46:21 +0000623 return wrapper, f
624
625 def test_default_update(self):
626 wrapper, f = self._default_update()
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000627 self.check_wrapper(wrapper, f)
Nick Coghlan98876832010-08-17 06:17:18 +0000628 self.assertIs(wrapper.__wrapped__, f)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000629 self.assertEqual(wrapper.__name__, 'f')
Meador Ingeff7f64c2011-12-11 22:37:31 -0600630 self.assertEqual(wrapper.__qualname__, f.__qualname__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000631 self.assertEqual(wrapper.attr, 'This is also a test')
Antoine Pitrou560f7642010-08-04 18:28:02 +0000632 self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation')
633 self.assertNotIn('b', wrapper.__annotations__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000634
R. David Murray378c0cf2010-02-24 01:46:21 +0000635 @unittest.skipIf(sys.flags.optimize >= 2,
636 "Docstrings are omitted with -O2 and above")
637 def test_default_update_doc(self):
638 wrapper, f = self._default_update()
639 self.assertEqual(wrapper.__doc__, 'This is a test')
640
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000641 def test_no_update(self):
642 def f():
643 """This is a test"""
644 pass
645 f.attr = 'This is also a test'
646 def wrapper():
647 pass
648 functools.update_wrapper(wrapper, f, (), ())
649 self.check_wrapper(wrapper, f, (), ())
650 self.assertEqual(wrapper.__name__, 'wrapper')
Meador Ingeff7f64c2011-12-11 22:37:31 -0600651 self.assertNotEqual(wrapper.__qualname__, f.__qualname__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000652 self.assertEqual(wrapper.__doc__, None)
Antoine Pitrou560f7642010-08-04 18:28:02 +0000653 self.assertEqual(wrapper.__annotations__, {})
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000654 self.assertFalse(hasattr(wrapper, 'attr'))
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000655
656 def test_selective_update(self):
657 def f():
658 pass
659 f.attr = 'This is a different test'
660 f.dict_attr = dict(a=1, b=2, c=3)
661 def wrapper():
662 pass
663 wrapper.dict_attr = {}
664 assign = ('attr',)
665 update = ('dict_attr',)
666 functools.update_wrapper(wrapper, f, assign, update)
667 self.check_wrapper(wrapper, f, assign, update)
668 self.assertEqual(wrapper.__name__, 'wrapper')
Meador Ingeff7f64c2011-12-11 22:37:31 -0600669 self.assertNotEqual(wrapper.__qualname__, f.__qualname__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000670 self.assertEqual(wrapper.__doc__, None)
671 self.assertEqual(wrapper.attr, 'This is a different test')
672 self.assertEqual(wrapper.dict_attr, f.dict_attr)
673
Nick Coghlan98876832010-08-17 06:17:18 +0000674 def test_missing_attributes(self):
675 def f():
676 pass
677 def wrapper():
678 pass
679 wrapper.dict_attr = {}
680 assign = ('attr',)
681 update = ('dict_attr',)
682 # Missing attributes on wrapped object are ignored
683 functools.update_wrapper(wrapper, f, assign, update)
684 self.assertNotIn('attr', wrapper.__dict__)
685 self.assertEqual(wrapper.dict_attr, {})
686 # Wrapper must have expected attributes for updating
687 del wrapper.dict_attr
688 with self.assertRaises(AttributeError):
689 functools.update_wrapper(wrapper, f, assign, update)
690 wrapper.dict_attr = 1
691 with self.assertRaises(AttributeError):
692 functools.update_wrapper(wrapper, f, assign, update)
693
Serhiy Storchaka9d0add02013-01-27 19:47:45 +0200694 @support.requires_docstrings
Nick Coghlan98876832010-08-17 06:17:18 +0000695 @unittest.skipIf(sys.flags.optimize >= 2,
696 "Docstrings are omitted with -O2 and above")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000697 def test_builtin_update(self):
698 # Test for bug #1576241
699 def wrapper():
700 pass
701 functools.update_wrapper(wrapper, max)
702 self.assertEqual(wrapper.__name__, 'max')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000703 self.assertTrue(wrapper.__doc__.startswith('max('))
Antoine Pitrou560f7642010-08-04 18:28:02 +0000704 self.assertEqual(wrapper.__annotations__, {})
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000705
Łukasz Langa6f692512013-06-05 12:20:24 +0200706
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000707class TestWraps(TestUpdateWrapper):
708
R. David Murray378c0cf2010-02-24 01:46:21 +0000709 def _default_update(self):
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000710 def f():
711 """This is a test"""
712 pass
713 f.attr = 'This is also a test'
Nick Coghlan24c05bc2013-07-15 21:13:08 +1000714 f.__wrapped__ = "This is still a bald faced lie"
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000715 @functools.wraps(f)
716 def wrapper():
717 pass
Meador Ingeff7f64c2011-12-11 22:37:31 -0600718 return wrapper, f
R. David Murray378c0cf2010-02-24 01:46:21 +0000719
720 def test_default_update(self):
Meador Ingeff7f64c2011-12-11 22:37:31 -0600721 wrapper, f = self._default_update()
Nick Coghlan24c05bc2013-07-15 21:13:08 +1000722 self.check_wrapper(wrapper, f)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000723 self.assertEqual(wrapper.__name__, 'f')
Meador Ingeff7f64c2011-12-11 22:37:31 -0600724 self.assertEqual(wrapper.__qualname__, f.__qualname__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000725 self.assertEqual(wrapper.attr, 'This is also a test')
726
Antoine Pitroub5b37142012-11-13 21:35:40 +0100727 @unittest.skipIf(sys.flags.optimize >= 2,
R. David Murray378c0cf2010-02-24 01:46:21 +0000728 "Docstrings are omitted with -O2 and above")
729 def test_default_update_doc(self):
Meador Ingeff7f64c2011-12-11 22:37:31 -0600730 wrapper, _ = self._default_update()
R. David Murray378c0cf2010-02-24 01:46:21 +0000731 self.assertEqual(wrapper.__doc__, 'This is a test')
732
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000733 def test_no_update(self):
734 def f():
735 """This is a test"""
736 pass
737 f.attr = 'This is also a test'
738 @functools.wraps(f, (), ())
739 def wrapper():
740 pass
741 self.check_wrapper(wrapper, f, (), ())
742 self.assertEqual(wrapper.__name__, 'wrapper')
Meador Ingeff7f64c2011-12-11 22:37:31 -0600743 self.assertNotEqual(wrapper.__qualname__, f.__qualname__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000744 self.assertEqual(wrapper.__doc__, None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000745 self.assertFalse(hasattr(wrapper, 'attr'))
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000746
747 def test_selective_update(self):
748 def f():
749 pass
750 f.attr = 'This is a different test'
751 f.dict_attr = dict(a=1, b=2, c=3)
752 def add_dict_attr(f):
753 f.dict_attr = {}
754 return f
755 assign = ('attr',)
756 update = ('dict_attr',)
757 @functools.wraps(f, assign, update)
758 @add_dict_attr
759 def wrapper():
760 pass
761 self.check_wrapper(wrapper, f, assign, update)
762 self.assertEqual(wrapper.__name__, 'wrapper')
Meador Ingeff7f64c2011-12-11 22:37:31 -0600763 self.assertNotEqual(wrapper.__qualname__, f.__qualname__)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000764 self.assertEqual(wrapper.__doc__, None)
765 self.assertEqual(wrapper.attr, 'This is a different test')
766 self.assertEqual(wrapper.dict_attr, f.dict_attr)
767
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000768
madman-bobe25d5fc2018-10-25 15:02:10 +0100769class TestReduce:
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000770 def test_reduce(self):
771 class Squares:
772 def __init__(self, max):
773 self.max = max
774 self.sofar = []
775
776 def __len__(self):
777 return len(self.sofar)
778
779 def __getitem__(self, i):
780 if not 0 <= i < self.max: raise IndexError
781 n = len(self.sofar)
782 while n <= i:
783 self.sofar.append(n*n)
784 n += 1
785 return self.sofar[i]
Alexander Belopolskye29e6bf2010-08-16 18:55:46 +0000786 def add(x, y):
787 return x + y
madman-bobe25d5fc2018-10-25 15:02:10 +0100788 self.assertEqual(self.reduce(add, ['a', 'b', 'c'], ''), 'abc')
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000789 self.assertEqual(
madman-bobe25d5fc2018-10-25 15:02:10 +0100790 self.reduce(add, [['a', 'c'], [], ['d', 'w']], []),
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000791 ['a','c','d','w']
792 )
madman-bobe25d5fc2018-10-25 15:02:10 +0100793 self.assertEqual(self.reduce(lambda x, y: x*y, range(2,8), 1), 5040)
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000794 self.assertEqual(
madman-bobe25d5fc2018-10-25 15:02:10 +0100795 self.reduce(lambda x, y: x*y, range(2,21), 1),
Guido van Rossume2a383d2007-01-15 16:59:06 +0000796 2432902008176640000
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000797 )
madman-bobe25d5fc2018-10-25 15:02:10 +0100798 self.assertEqual(self.reduce(add, Squares(10)), 285)
799 self.assertEqual(self.reduce(add, Squares(10), 0), 285)
800 self.assertEqual(self.reduce(add, Squares(0), 0), 0)
801 self.assertRaises(TypeError, self.reduce)
802 self.assertRaises(TypeError, self.reduce, 42, 42)
803 self.assertRaises(TypeError, self.reduce, 42, 42, 42)
804 self.assertEqual(self.reduce(42, "1"), "1") # func is never called with one item
805 self.assertEqual(self.reduce(42, "", "1"), "1") # func is never called with one item
806 self.assertRaises(TypeError, self.reduce, 42, (42, 42))
807 self.assertRaises(TypeError, self.reduce, add, []) # arg 2 must not be empty sequence with no initial value
808 self.assertRaises(TypeError, self.reduce, add, "")
809 self.assertRaises(TypeError, self.reduce, add, ())
810 self.assertRaises(TypeError, self.reduce, add, object())
Alexander Belopolskye29e6bf2010-08-16 18:55:46 +0000811
812 class TestFailingIter:
813 def __iter__(self):
814 raise RuntimeError
madman-bobe25d5fc2018-10-25 15:02:10 +0100815 self.assertRaises(RuntimeError, self.reduce, add, TestFailingIter())
Alexander Belopolskye29e6bf2010-08-16 18:55:46 +0000816
madman-bobe25d5fc2018-10-25 15:02:10 +0100817 self.assertEqual(self.reduce(add, [], None), None)
818 self.assertEqual(self.reduce(add, [], 42), 42)
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000819
820 class BadSeq:
821 def __getitem__(self, index):
822 raise ValueError
madman-bobe25d5fc2018-10-25 15:02:10 +0100823 self.assertRaises(ValueError, self.reduce, 42, BadSeq())
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000824
825 # Test reduce()'s use of iterators.
826 def test_iterator_usage(self):
827 class SequenceClass:
828 def __init__(self, n):
829 self.n = n
830 def __getitem__(self, i):
831 if 0 <= i < self.n:
832 return i
833 else:
834 raise IndexError
Guido van Rossumd8faa362007-04-27 19:54:29 +0000835
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000836 from operator import add
madman-bobe25d5fc2018-10-25 15:02:10 +0100837 self.assertEqual(self.reduce(add, SequenceClass(5)), 10)
838 self.assertEqual(self.reduce(add, SequenceClass(5), 42), 52)
839 self.assertRaises(TypeError, self.reduce, add, SequenceClass(0))
840 self.assertEqual(self.reduce(add, SequenceClass(0), 42), 42)
841 self.assertEqual(self.reduce(add, SequenceClass(1)), 0)
842 self.assertEqual(self.reduce(add, SequenceClass(1), 42), 42)
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000843
844 d = {"one": 1, "two": 2, "three": 3}
madman-bobe25d5fc2018-10-25 15:02:10 +0100845 self.assertEqual(self.reduce(add, d), "".join(d.keys()))
846
847
848@unittest.skipUnless(c_functools, 'requires the C _functools module')
849class TestReduceC(TestReduce, unittest.TestCase):
850 if c_functools:
851 reduce = c_functools.reduce
852
853
854class TestReducePy(TestReduce, unittest.TestCase):
855 reduce = staticmethod(py_functools.reduce)
Guido van Rossum0919a1a2006-08-26 20:49:04 +0000856
Łukasz Langa6f692512013-06-05 12:20:24 +0200857
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200858class TestCmpToKey:
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700859
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000860 def test_cmp_to_key(self):
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700861 def cmp1(x, y):
862 return (x > y) - (x < y)
Antoine Pitroub5b37142012-11-13 21:35:40 +0100863 key = self.cmp_to_key(cmp1)
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700864 self.assertEqual(key(3), key(3))
865 self.assertGreater(key(3), key(1))
Antoine Pitroub5b37142012-11-13 21:35:40 +0100866 self.assertGreaterEqual(key(3), key(3))
867
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700868 def cmp2(x, y):
869 return int(x) - int(y)
Antoine Pitroub5b37142012-11-13 21:35:40 +0100870 key = self.cmp_to_key(cmp2)
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700871 self.assertEqual(key(4.0), key('4'))
872 self.assertLess(key(2), key('35'))
Antoine Pitroub5b37142012-11-13 21:35:40 +0100873 self.assertLessEqual(key(2), key('35'))
874 self.assertNotEqual(key(2), key('35'))
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700875
876 def test_cmp_to_key_arguments(self):
877 def cmp1(x, y):
878 return (x > y) - (x < y)
Antoine Pitroub5b37142012-11-13 21:35:40 +0100879 key = self.cmp_to_key(mycmp=cmp1)
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700880 self.assertEqual(key(obj=3), key(obj=3))
881 self.assertGreater(key(obj=3), key(obj=1))
882 with self.assertRaises((TypeError, AttributeError)):
883 key(3) > 1 # rhs is not a K object
884 with self.assertRaises((TypeError, AttributeError)):
885 1 < key(3) # lhs is not a K object
886 with self.assertRaises(TypeError):
Antoine Pitroub5b37142012-11-13 21:35:40 +0100887 key = self.cmp_to_key() # too few args
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700888 with self.assertRaises(TypeError):
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200889 key = self.cmp_to_key(cmp1, None) # too many args
Antoine Pitroub5b37142012-11-13 21:35:40 +0100890 key = self.cmp_to_key(cmp1)
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700891 with self.assertRaises(TypeError):
892 key() # too few args
893 with self.assertRaises(TypeError):
894 key(None, None) # too many args
895
896 def test_bad_cmp(self):
897 def cmp1(x, y):
898 raise ZeroDivisionError
Antoine Pitroub5b37142012-11-13 21:35:40 +0100899 key = self.cmp_to_key(cmp1)
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700900 with self.assertRaises(ZeroDivisionError):
901 key(3) > key(1)
902
903 class BadCmp:
904 def __lt__(self, other):
905 raise ZeroDivisionError
906 def cmp1(x, y):
907 return BadCmp()
908 with self.assertRaises(ZeroDivisionError):
909 key(3) > key(1)
910
911 def test_obj_field(self):
912 def cmp1(x, y):
913 return (x > y) - (x < y)
Antoine Pitroub5b37142012-11-13 21:35:40 +0100914 key = self.cmp_to_key(mycmp=cmp1)
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700915 self.assertEqual(key(50).obj, 50)
916
917 def test_sort_int(self):
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000918 def mycmp(x, y):
919 return y - x
Antoine Pitroub5b37142012-11-13 21:35:40 +0100920 self.assertEqual(sorted(range(5), key=self.cmp_to_key(mycmp)),
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000921 [4, 3, 2, 1, 0])
Guido van Rossumd8faa362007-04-27 19:54:29 +0000922
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700923 def test_sort_int_str(self):
924 def mycmp(x, y):
925 x, y = int(x), int(y)
926 return (x > y) - (x < y)
927 values = [5, '3', 7, 2, '0', '1', 4, '10', 1]
Antoine Pitroub5b37142012-11-13 21:35:40 +0100928 values = sorted(values, key=self.cmp_to_key(mycmp))
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700929 self.assertEqual([int(value) for value in values],
930 [0, 1, 1, 2, 3, 4, 5, 7, 10])
931
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000932 def test_hash(self):
933 def mycmp(x, y):
934 return y - x
Antoine Pitroub5b37142012-11-13 21:35:40 +0100935 key = self.cmp_to_key(mycmp)
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000936 k = key(10)
Raymond Hettinger7ab9e222011-04-05 02:33:54 -0700937 self.assertRaises(TypeError, hash, k)
Serhiy Storchaka2e576f52017-04-24 09:05:00 +0300938 self.assertNotIsInstance(k, collections.abc.Hashable)
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000939
Łukasz Langa6f692512013-06-05 12:20:24 +0200940
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200941@unittest.skipUnless(c_functools, 'requires the C _functools module')
942class TestCmpToKeyC(TestCmpToKey, unittest.TestCase):
943 if c_functools:
944 cmp_to_key = c_functools.cmp_to_key
Antoine Pitroub5b37142012-11-13 21:35:40 +0100945
Łukasz Langa6f692512013-06-05 12:20:24 +0200946
Serhiy Storchakaca4220b2013-02-05 22:12:59 +0200947class TestCmpToKeyPy(TestCmpToKey, unittest.TestCase):
Antoine Pitroub5b37142012-11-13 21:35:40 +0100948 cmp_to_key = staticmethod(py_functools.cmp_to_key)
949
Łukasz Langa6f692512013-06-05 12:20:24 +0200950
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000951class TestTotalOrdering(unittest.TestCase):
952
953 def test_total_ordering_lt(self):
954 @functools.total_ordering
955 class A:
956 def __init__(self, value):
957 self.value = value
958 def __lt__(self, other):
959 return self.value < other.value
Raymond Hettinger23f9fc32011-01-08 07:01:56 +0000960 def __eq__(self, other):
961 return self.value == other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +0000962 self.assertTrue(A(1) < A(2))
963 self.assertTrue(A(2) > A(1))
964 self.assertTrue(A(1) <= A(2))
965 self.assertTrue(A(2) >= A(1))
966 self.assertTrue(A(2) <= A(2))
967 self.assertTrue(A(2) >= A(2))
Nick Coghlanf05d9812013-10-02 00:02:03 +1000968 self.assertFalse(A(1) > A(2))
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000969
970 def test_total_ordering_le(self):
971 @functools.total_ordering
972 class A:
973 def __init__(self, value):
974 self.value = value
975 def __le__(self, other):
976 return self.value <= other.value
Raymond Hettinger23f9fc32011-01-08 07:01:56 +0000977 def __eq__(self, other):
978 return self.value == other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +0000979 self.assertTrue(A(1) < A(2))
980 self.assertTrue(A(2) > A(1))
981 self.assertTrue(A(1) <= A(2))
982 self.assertTrue(A(2) >= A(1))
983 self.assertTrue(A(2) <= A(2))
984 self.assertTrue(A(2) >= A(2))
Nick Coghlanf05d9812013-10-02 00:02:03 +1000985 self.assertFalse(A(1) >= A(2))
Raymond Hettingerc50846a2010-04-05 18:56:31 +0000986
987 def test_total_ordering_gt(self):
988 @functools.total_ordering
989 class A:
990 def __init__(self, value):
991 self.value = value
992 def __gt__(self, other):
993 return self.value > other.value
Raymond Hettinger23f9fc32011-01-08 07:01:56 +0000994 def __eq__(self, other):
995 return self.value == other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +0000996 self.assertTrue(A(1) < A(2))
997 self.assertTrue(A(2) > A(1))
998 self.assertTrue(A(1) <= A(2))
999 self.assertTrue(A(2) >= A(1))
1000 self.assertTrue(A(2) <= A(2))
1001 self.assertTrue(A(2) >= A(2))
Nick Coghlanf05d9812013-10-02 00:02:03 +10001002 self.assertFalse(A(2) < A(1))
Raymond Hettingerc50846a2010-04-05 18:56:31 +00001003
1004 def test_total_ordering_ge(self):
1005 @functools.total_ordering
1006 class A:
1007 def __init__(self, value):
1008 self.value = value
1009 def __ge__(self, other):
1010 return self.value >= other.value
Raymond Hettinger23f9fc32011-01-08 07:01:56 +00001011 def __eq__(self, other):
1012 return self.value == other.value
Ezio Melottib3aedd42010-11-20 19:04:17 +00001013 self.assertTrue(A(1) < A(2))
1014 self.assertTrue(A(2) > A(1))
1015 self.assertTrue(A(1) <= A(2))
1016 self.assertTrue(A(2) >= A(1))
1017 self.assertTrue(A(2) <= A(2))
1018 self.assertTrue(A(2) >= A(2))
Nick Coghlanf05d9812013-10-02 00:02:03 +10001019 self.assertFalse(A(2) <= A(1))
Raymond Hettingerc50846a2010-04-05 18:56:31 +00001020
1021 def test_total_ordering_no_overwrite(self):
1022 # new methods should not overwrite existing
1023 @functools.total_ordering
1024 class A(int):
Benjamin Peterson9c2930e2010-08-23 17:40:33 +00001025 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +00001026 self.assertTrue(A(1) < A(2))
1027 self.assertTrue(A(2) > A(1))
1028 self.assertTrue(A(1) <= A(2))
1029 self.assertTrue(A(2) >= A(1))
1030 self.assertTrue(A(2) <= A(2))
1031 self.assertTrue(A(2) >= A(2))
Raymond Hettinger9c323f82005-02-28 19:39:44 +00001032
Benjamin Peterson42ebee32010-04-11 01:43:16 +00001033 def test_no_operations_defined(self):
1034 with self.assertRaises(ValueError):
1035 @functools.total_ordering
1036 class A:
1037 pass
Raymond Hettinger9c323f82005-02-28 19:39:44 +00001038
Nick Coghlanf05d9812013-10-02 00:02:03 +10001039 def test_type_error_when_not_implemented(self):
1040 # bug 10042; ensure stack overflow does not occur
1041 # when decorated types return NotImplemented
Raymond Hettinger23f9fc32011-01-08 07:01:56 +00001042 @functools.total_ordering
Nick Coghlanf05d9812013-10-02 00:02:03 +10001043 class ImplementsLessThan:
Raymond Hettinger23f9fc32011-01-08 07:01:56 +00001044 def __init__(self, value):
1045 self.value = value
1046 def __eq__(self, other):
Nick Coghlanf05d9812013-10-02 00:02:03 +10001047 if isinstance(other, ImplementsLessThan):
Raymond Hettinger23f9fc32011-01-08 07:01:56 +00001048 return self.value == other.value
1049 return False
1050 def __lt__(self, other):
Nick Coghlanf05d9812013-10-02 00:02:03 +10001051 if isinstance(other, ImplementsLessThan):
Raymond Hettinger23f9fc32011-01-08 07:01:56 +00001052 return self.value < other.value
Nick Coghlanf05d9812013-10-02 00:02:03 +10001053 return NotImplemented
Raymond Hettinger23f9fc32011-01-08 07:01:56 +00001054
Nick Coghlanf05d9812013-10-02 00:02:03 +10001055 @functools.total_ordering
1056 class ImplementsGreaterThan:
1057 def __init__(self, value):
1058 self.value = value
1059 def __eq__(self, other):
1060 if isinstance(other, ImplementsGreaterThan):
1061 return self.value == other.value
1062 return False
1063 def __gt__(self, other):
1064 if isinstance(other, ImplementsGreaterThan):
1065 return self.value > other.value
1066 return NotImplemented
1067
1068 @functools.total_ordering
1069 class ImplementsLessThanEqualTo:
1070 def __init__(self, value):
1071 self.value = value
1072 def __eq__(self, other):
1073 if isinstance(other, ImplementsLessThanEqualTo):
1074 return self.value == other.value
1075 return False
1076 def __le__(self, other):
1077 if isinstance(other, ImplementsLessThanEqualTo):
1078 return self.value <= other.value
1079 return NotImplemented
1080
1081 @functools.total_ordering
1082 class ImplementsGreaterThanEqualTo:
1083 def __init__(self, value):
1084 self.value = value
1085 def __eq__(self, other):
1086 if isinstance(other, ImplementsGreaterThanEqualTo):
1087 return self.value == other.value
1088 return False
1089 def __ge__(self, other):
1090 if isinstance(other, ImplementsGreaterThanEqualTo):
1091 return self.value >= other.value
1092 return NotImplemented
1093
1094 @functools.total_ordering
1095 class ComparatorNotImplemented:
1096 def __init__(self, value):
1097 self.value = value
1098 def __eq__(self, other):
1099 if isinstance(other, ComparatorNotImplemented):
1100 return self.value == other.value
1101 return False
1102 def __lt__(self, other):
1103 return NotImplemented
1104
1105 with self.subTest("LT < 1"), self.assertRaises(TypeError):
1106 ImplementsLessThan(-1) < 1
1107
1108 with self.subTest("LT < LE"), self.assertRaises(TypeError):
1109 ImplementsLessThan(0) < ImplementsLessThanEqualTo(0)
1110
1111 with self.subTest("LT < GT"), self.assertRaises(TypeError):
1112 ImplementsLessThan(1) < ImplementsGreaterThan(1)
1113
1114 with self.subTest("LE <= LT"), self.assertRaises(TypeError):
1115 ImplementsLessThanEqualTo(2) <= ImplementsLessThan(2)
1116
1117 with self.subTest("LE <= GE"), self.assertRaises(TypeError):
1118 ImplementsLessThanEqualTo(3) <= ImplementsGreaterThanEqualTo(3)
1119
1120 with self.subTest("GT > GE"), self.assertRaises(TypeError):
1121 ImplementsGreaterThan(4) > ImplementsGreaterThanEqualTo(4)
1122
1123 with self.subTest("GT > LT"), self.assertRaises(TypeError):
1124 ImplementsGreaterThan(5) > ImplementsLessThan(5)
1125
1126 with self.subTest("GE >= GT"), self.assertRaises(TypeError):
1127 ImplementsGreaterThanEqualTo(6) >= ImplementsGreaterThan(6)
1128
1129 with self.subTest("GE >= LE"), self.assertRaises(TypeError):
1130 ImplementsGreaterThanEqualTo(7) >= ImplementsLessThanEqualTo(7)
1131
1132 with self.subTest("GE when equal"):
1133 a = ComparatorNotImplemented(8)
1134 b = ComparatorNotImplemented(8)
1135 self.assertEqual(a, b)
1136 with self.assertRaises(TypeError):
1137 a >= b
1138
1139 with self.subTest("LE when equal"):
1140 a = ComparatorNotImplemented(9)
1141 b = ComparatorNotImplemented(9)
1142 self.assertEqual(a, b)
1143 with self.assertRaises(TypeError):
1144 a <= b
Łukasz Langa6f692512013-06-05 12:20:24 +02001145
Serhiy Storchaka697a5262015-01-01 15:23:12 +02001146 def test_pickle(self):
Serhiy Storchaka92bb90a2016-09-22 11:39:25 +03001147 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
Serhiy Storchaka697a5262015-01-01 15:23:12 +02001148 for name in '__lt__', '__gt__', '__le__', '__ge__':
1149 with self.subTest(method=name, proto=proto):
1150 method = getattr(Orderable_LT, name)
1151 method_copy = pickle.loads(pickle.dumps(method, proto))
1152 self.assertIs(method_copy, method)
1153
1154@functools.total_ordering
1155class Orderable_LT:
1156 def __init__(self, value):
1157 self.value = value
1158 def __lt__(self, other):
1159 return self.value < other.value
1160 def __eq__(self, other):
1161 return self.value == other.value
1162
1163
Pablo Galindo99e6c262020-01-23 15:29:52 +00001164class TestTopologicalSort(unittest.TestCase):
1165
1166 def _test_graph(self, graph, expected):
1167
1168 def static_order_with_groups(ts):
1169 ts.prepare()
1170 while ts.is_active():
1171 nodes = ts.get_ready()
1172 for node in nodes:
1173 ts.done(node)
1174 yield nodes
1175
1176 ts = functools.TopologicalSorter(graph)
1177 self.assertEqual(list(static_order_with_groups(ts)), list(expected))
1178
1179 ts = functools.TopologicalSorter(graph)
1180 self.assertEqual(list(ts.static_order()), list(chain(*expected)))
1181
1182 def _assert_cycle(self, graph, cycle):
1183 ts = functools.TopologicalSorter()
1184 for node, dependson in graph.items():
1185 ts.add(node, *dependson)
1186 try:
1187 ts.prepare()
1188 except functools.CycleError as e:
1189 msg, seq = e.args
1190 self.assertIn(' '.join(map(str, cycle)),
1191 ' '.join(map(str, seq * 2)))
1192 else:
1193 raise
1194
1195 def test_simple_cases(self):
1196 self._test_graph(
1197 {2: {11},
1198 9: {11, 8},
1199 10: {11, 3},
1200 11: {7, 5},
1201 8: {7, 3}},
1202 [(3, 5, 7), (11, 8), (2, 10, 9)]
1203 )
1204
1205 self._test_graph({1: {}}, [(1,)])
1206
1207 self._test_graph({x: {x+1} for x in range(10)},
1208 [(x,) for x in range(10, -1, -1)])
1209
1210 self._test_graph({2: {3}, 3: {4}, 4: {5}, 5: {1},
1211 11: {12}, 12: {13}, 13: {14}, 14: {15}},
1212 [(1, 15), (5, 14), (4, 13), (3, 12), (2, 11)])
1213
1214 self._test_graph({
1215 0: [1, 2],
1216 1: [3],
1217 2: [5, 6],
1218 3: [4],
1219 4: [9],
1220 5: [3],
1221 6: [7],
1222 7: [8],
1223 8: [4],
1224 9: []
1225 },
1226 [(9,), (4,), (3, 8), (1, 5, 7), (6,), (2,), (0,)]
1227 )
1228
1229 self._test_graph({
1230 0: [1, 2],
1231 1: [],
1232 2: [3],
1233 3: []
1234 },
1235 [(1, 3), (2,), (0,)]
1236 )
1237
1238 self._test_graph({
1239 0: [1, 2],
1240 1: [],
1241 2: [3],
1242 3: [],
1243 4: [5],
1244 5: [6],
1245 6: []
1246 },
1247 [(1, 3, 6), (2, 5), (0, 4)]
1248 )
1249
1250 def test_no_dependencies(self):
1251 self._test_graph(
1252 {1: {2},
1253 3: {4},
1254 5: {6}},
1255 [(2, 4, 6), (1, 3, 5)]
1256 )
1257
1258 self._test_graph(
1259 {1: set(),
1260 3: set(),
1261 5: set()},
1262 [(1, 3, 5)]
1263 )
1264
1265 def test_the_node_multiple_times(self):
1266 # Test same node multiple times in dependencies
1267 self._test_graph({1: {2}, 3: {4}, 0: [2, 4, 4, 4, 4, 4]},
1268 [(2, 4), (1, 3, 0)])
1269
1270 # Test adding the same dependency multiple times
1271 ts = functools.TopologicalSorter()
1272 ts.add(1, 2)
1273 ts.add(1, 2)
1274 ts.add(1, 2)
1275 self.assertEqual([*ts.static_order()], [2, 1])
1276
1277 def test_graph_with_iterables(self):
1278 dependson = (2*x + 1 for x in range(5))
1279 ts = functools.TopologicalSorter({0: dependson})
1280 self.assertEqual(list(ts.static_order()), [1, 3, 5, 7, 9, 0])
1281
1282 def test_add_dependencies_for_same_node_incrementally(self):
1283 # Test same node multiple times
1284 ts = functools.TopologicalSorter()
1285 ts.add(1, 2)
1286 ts.add(1, 3)
1287 ts.add(1, 4)
1288 ts.add(1, 5)
1289
1290 ts2 = functools.TopologicalSorter({1: {2, 3, 4, 5}})
1291 self.assertEqual([*ts.static_order()], [*ts2.static_order()])
1292
1293 def test_empty(self):
1294 self._test_graph({}, [])
1295
1296 def test_cycle(self):
1297 # Self cycle
1298 self._assert_cycle({1: {1}}, [1, 1])
1299 # Simple cycle
1300 self._assert_cycle({1: {2}, 2: {1}}, [1, 2, 1])
1301 # Indirect cycle
1302 self._assert_cycle({1: {2}, 2: {3}, 3: {1}}, [1, 3, 2, 1])
1303 # not all elements involved in a cycle
1304 self._assert_cycle({1: {2}, 2: {3}, 3: {1}, 5: {4}, 4: {6}}, [1, 3, 2, 1])
1305 # Multiple cycles
1306 self._assert_cycle({1: {2}, 2: {1}, 3: {4}, 4: {5}, 6: {7}, 7: {6}},
1307 [1, 2, 1])
1308 # Cycle in the middle of the graph
1309 self._assert_cycle({1: {2}, 2: {3}, 3: {2, 4}, 4: {5}}, [3, 2])
1310
1311 def test_calls_before_prepare(self):
1312 ts = functools.TopologicalSorter()
1313
1314 with self.assertRaisesRegex(ValueError, r"prepare\(\) must be called first"):
1315 ts.get_ready()
1316 with self.assertRaisesRegex(ValueError, r"prepare\(\) must be called first"):
1317 ts.done(3)
1318 with self.assertRaisesRegex(ValueError, r"prepare\(\) must be called first"):
1319 ts.is_active()
1320
1321 def test_prepare_multiple_times(self):
1322 ts = functools.TopologicalSorter()
1323 ts.prepare()
1324 with self.assertRaisesRegex(ValueError, r"cannot prepare\(\) more than once"):
1325 ts.prepare()
1326
1327 def test_invalid_nodes_in_done(self):
1328 ts = functools.TopologicalSorter()
1329 ts.add(1, 2, 3, 4)
1330 ts.add(2, 3, 4)
1331 ts.prepare()
1332 ts.get_ready()
1333
1334 with self.assertRaisesRegex(ValueError, "node 2 was not passed out"):
1335 ts.done(2)
1336 with self.assertRaisesRegex(ValueError, r"node 24 was not added using add\(\)"):
1337 ts.done(24)
1338
1339 def test_done(self):
1340 ts = functools.TopologicalSorter()
1341 ts.add(1, 2, 3, 4)
1342 ts.add(2, 3)
1343 ts.prepare()
1344
1345 self.assertEqual(ts.get_ready(), (3, 4))
1346 # If we don't mark anything as done, get_ready() returns nothing
1347 self.assertEqual(ts.get_ready(), ())
1348 ts.done(3)
1349 # Now 2 becomes available as 3 is done
1350 self.assertEqual(ts.get_ready(), (2,))
1351 self.assertEqual(ts.get_ready(), ())
1352 ts.done(4)
1353 ts.done(2)
1354 # Only 1 is missing
1355 self.assertEqual(ts.get_ready(), (1,))
1356 self.assertEqual(ts.get_ready(), ())
1357 ts.done(1)
1358 self.assertEqual(ts.get_ready(), ())
1359 self.assertFalse(ts.is_active())
1360
1361 def test_is_active(self):
1362 ts = functools.TopologicalSorter()
1363 ts.add(1, 2)
1364 ts.prepare()
1365
1366 self.assertTrue(ts.is_active())
1367 self.assertEqual(ts.get_ready(), (2,))
1368 self.assertTrue(ts.is_active())
1369 ts.done(2)
1370 self.assertTrue(ts.is_active())
1371 self.assertEqual(ts.get_ready(), (1,))
1372 self.assertTrue(ts.is_active())
1373 ts.done(1)
1374 self.assertFalse(ts.is_active())
1375
1376 def test_not_hashable_nodes(self):
1377 ts = functools.TopologicalSorter()
1378 self.assertRaises(TypeError, ts.add, dict(), 1)
1379 self.assertRaises(TypeError, ts.add, 1, dict())
1380 self.assertRaises(TypeError, ts.add, dict(), dict())
1381
1382 def test_order_of_insertion_does_not_matter_between_groups(self):
1383 def get_groups(ts):
1384 ts.prepare()
1385 while ts.is_active():
1386 nodes = ts.get_ready()
1387 ts.done(*nodes)
1388 yield set(nodes)
1389
1390 ts = functools.TopologicalSorter()
1391 ts.add(3, 2, 1)
1392 ts.add(1, 0)
1393 ts.add(4, 5)
1394 ts.add(6, 7)
1395 ts.add(4, 7)
1396
1397 ts2 = functools.TopologicalSorter()
1398 ts2.add(1, 0)
1399 ts2.add(3, 2, 1)
1400 ts2.add(4, 7)
1401 ts2.add(6, 7)
1402 ts2.add(4, 5)
1403
1404 self.assertEqual(list(get_groups(ts)), list(get_groups(ts2)))
1405
1406 def test_static_order_does_not_change_with_the_hash_seed(self):
1407 def check_order_with_hash_seed(seed):
1408 code = """if 1:
1409 import functools
1410 ts = functools.TopologicalSorter()
1411 ts.add('blech', 'bluch', 'hola')
1412 ts.add('abcd', 'blech', 'bluch', 'a', 'b')
1413 ts.add('a', 'a string', 'something', 'b')
1414 ts.add('bluch', 'hola', 'abcde', 'a', 'b')
1415 print(list(ts.static_order()))
1416 """
1417 env = os.environ.copy()
1418 # signal to assert_python not to do a copy
1419 # of os.environ on its own
1420 env['__cleanenv'] = True
1421 env['PYTHONHASHSEED'] = str(seed)
1422 out = assert_python_ok('-c', code, **env)
1423 return out
1424
1425 run1 = check_order_with_hash_seed(1234)
1426 run2 = check_order_with_hash_seed(31415)
1427
1428 self.assertNotEqual(run1, "")
1429 self.assertNotEqual(run2, "")
1430 self.assertEqual(run1, run2)
1431
1432
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001433class TestLRU:
Georg Brandl2e7346a2010-07-31 18:09:23 +00001434
1435 def test_lru(self):
1436 def orig(x, y):
Antoine Pitroub5b37142012-11-13 21:35:40 +01001437 return 3 * x + y
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001438 f = self.module.lru_cache(maxsize=20)(orig)
Raymond Hettinger7496b412010-11-30 19:15:45 +00001439 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001440 self.assertEqual(maxsize, 20)
1441 self.assertEqual(currsize, 0)
1442 self.assertEqual(hits, 0)
1443 self.assertEqual(misses, 0)
Georg Brandl2e7346a2010-07-31 18:09:23 +00001444
1445 domain = range(5)
1446 for i in range(1000):
1447 x, y = choice(domain), choice(domain)
1448 actual = f(x, y)
1449 expected = orig(x, y)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001450 self.assertEqual(actual, expected)
Raymond Hettinger7496b412010-11-30 19:15:45 +00001451 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001452 self.assertTrue(hits > misses)
1453 self.assertEqual(hits + misses, 1000)
1454 self.assertEqual(currsize, 20)
Georg Brandl2e7346a2010-07-31 18:09:23 +00001455
Raymond Hettinger02566ec2010-09-04 22:46:06 +00001456 f.cache_clear() # test clearing
Raymond Hettinger7496b412010-11-30 19:15:45 +00001457 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001458 self.assertEqual(hits, 0)
1459 self.assertEqual(misses, 0)
1460 self.assertEqual(currsize, 0)
Georg Brandl2e7346a2010-07-31 18:09:23 +00001461 f(x, y)
Raymond Hettinger7496b412010-11-30 19:15:45 +00001462 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001463 self.assertEqual(hits, 0)
1464 self.assertEqual(misses, 1)
1465 self.assertEqual(currsize, 1)
Georg Brandl2e7346a2010-07-31 18:09:23 +00001466
Nick Coghlan98876832010-08-17 06:17:18 +00001467 # Test bypassing the cache
1468 self.assertIs(f.__wrapped__, orig)
1469 f.__wrapped__(x, y)
Raymond Hettinger7496b412010-11-30 19:15:45 +00001470 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001471 self.assertEqual(hits, 0)
1472 self.assertEqual(misses, 1)
1473 self.assertEqual(currsize, 1)
Nick Coghlan98876832010-08-17 06:17:18 +00001474
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001475 # test size zero (which means "never-cache")
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001476 @self.module.lru_cache(0)
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001477 def f():
1478 nonlocal f_cnt
1479 f_cnt += 1
1480 return 20
Nick Coghlan234515a2010-11-30 06:19:46 +00001481 self.assertEqual(f.cache_info().maxsize, 0)
Raymond Hettingerf3098282010-08-15 03:30:45 +00001482 f_cnt = 0
1483 for i in range(5):
1484 self.assertEqual(f(), 20)
1485 self.assertEqual(f_cnt, 5)
Raymond Hettinger7496b412010-11-30 19:15:45 +00001486 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001487 self.assertEqual(hits, 0)
1488 self.assertEqual(misses, 5)
1489 self.assertEqual(currsize, 0)
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001490
1491 # test size one
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001492 @self.module.lru_cache(1)
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001493 def f():
1494 nonlocal f_cnt
1495 f_cnt += 1
1496 return 20
Nick Coghlan234515a2010-11-30 06:19:46 +00001497 self.assertEqual(f.cache_info().maxsize, 1)
Raymond Hettingerf3098282010-08-15 03:30:45 +00001498 f_cnt = 0
1499 for i in range(5):
1500 self.assertEqual(f(), 20)
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001501 self.assertEqual(f_cnt, 1)
Raymond Hettinger7496b412010-11-30 19:15:45 +00001502 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001503 self.assertEqual(hits, 4)
1504 self.assertEqual(misses, 1)
1505 self.assertEqual(currsize, 1)
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001506
Raymond Hettingerf3098282010-08-15 03:30:45 +00001507 # test size two
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001508 @self.module.lru_cache(2)
Raymond Hettingerf3098282010-08-15 03:30:45 +00001509 def f(x):
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001510 nonlocal f_cnt
1511 f_cnt += 1
Raymond Hettingerf3098282010-08-15 03:30:45 +00001512 return x*10
Nick Coghlan234515a2010-11-30 06:19:46 +00001513 self.assertEqual(f.cache_info().maxsize, 2)
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001514 f_cnt = 0
Raymond Hettingerf3098282010-08-15 03:30:45 +00001515 for x in 7, 9, 7, 9, 7, 9, 8, 8, 8, 9, 9, 9, 8, 8, 8, 7:
1516 # * * * *
1517 self.assertEqual(f(x), x*10)
1518 self.assertEqual(f_cnt, 4)
Raymond Hettinger7496b412010-11-30 19:15:45 +00001519 hits, misses, maxsize, currsize = f.cache_info()
Nick Coghlan234515a2010-11-30 06:19:46 +00001520 self.assertEqual(hits, 12)
1521 self.assertEqual(misses, 4)
1522 self.assertEqual(currsize, 2)
Raymond Hettinger0f56e902010-08-14 23:52:08 +00001523
Raymond Hettingerb8218682019-05-26 11:27:35 -07001524 def test_lru_no_args(self):
1525 @self.module.lru_cache
1526 def square(x):
1527 return x ** 2
1528
1529 self.assertEqual(list(map(square, [10, 20, 10])),
1530 [100, 400, 100])
1531 self.assertEqual(square.cache_info().hits, 1)
1532 self.assertEqual(square.cache_info().misses, 2)
1533 self.assertEqual(square.cache_info().maxsize, 128)
1534 self.assertEqual(square.cache_info().currsize, 2)
1535
Raymond Hettingerd8080c02019-01-26 03:02:00 -05001536 def test_lru_bug_35780(self):
1537 # C version of the lru_cache was not checking to see if
1538 # the user function call has already modified the cache
1539 # (this arises in recursive calls and in multi-threading).
1540 # This cause the cache to have orphan links not referenced
1541 # by the cache dictionary.
1542
1543 once = True # Modified by f(x) below
1544
1545 @self.module.lru_cache(maxsize=10)
1546 def f(x):
1547 nonlocal once
1548 rv = f'.{x}.'
1549 if x == 20 and once:
1550 once = False
1551 rv = f(x)
1552 return rv
1553
1554 # Fill the cache
1555 for x in range(15):
1556 self.assertEqual(f(x), f'.{x}.')
1557 self.assertEqual(f.cache_info().currsize, 10)
1558
1559 # Make a recursive call and make sure the cache remains full
1560 self.assertEqual(f(20), '.20.')
1561 self.assertEqual(f.cache_info().currsize, 10)
1562
Raymond Hettinger14adbd42019-04-20 07:20:44 -10001563 def test_lru_bug_36650(self):
1564 # C version of lru_cache was treating a call with an empty **kwargs
1565 # dictionary as being distinct from a call with no keywords at all.
1566 # This did not result in an incorrect answer, but it did trigger
1567 # an unexpected cache miss.
1568
1569 @self.module.lru_cache()
1570 def f(x):
1571 pass
1572
1573 f(0)
1574 f(0, **{})
1575 self.assertEqual(f.cache_info().hits, 1)
1576
Raymond Hettingerd191ef22017-01-07 20:44:48 -08001577 def test_lru_hash_only_once(self):
1578 # To protect against weird reentrancy bugs and to improve
1579 # efficiency when faced with slow __hash__ methods, the
1580 # LRU cache guarantees that it will only call __hash__
1581 # only once per use as an argument to the cached function.
1582
1583 @self.module.lru_cache(maxsize=1)
1584 def f(x, y):
1585 return x * 3 + y
1586
1587 # Simulate the integer 5
1588 mock_int = unittest.mock.Mock()
1589 mock_int.__mul__ = unittest.mock.Mock(return_value=15)
1590 mock_int.__hash__ = unittest.mock.Mock(return_value=999)
1591
1592 # Add to cache: One use as an argument gives one call
Raymond Hettinger5eed36f2017-01-07 20:53:09 -08001593 self.assertEqual(f(mock_int, 1), 16)
1594 self.assertEqual(mock_int.__hash__.call_count, 1)
1595 self.assertEqual(f.cache_info(), (0, 1, 1, 1))
Raymond Hettingerd191ef22017-01-07 20:44:48 -08001596
1597 # Cache hit: One use as an argument gives one additional call
Raymond Hettinger5eed36f2017-01-07 20:53:09 -08001598 self.assertEqual(f(mock_int, 1), 16)
1599 self.assertEqual(mock_int.__hash__.call_count, 2)
1600 self.assertEqual(f.cache_info(), (1, 1, 1, 1))
Raymond Hettingerd191ef22017-01-07 20:44:48 -08001601
Ville Skyttä49b27342017-08-03 09:00:59 +03001602 # Cache eviction: No use as an argument gives no additional call
Raymond Hettinger5eed36f2017-01-07 20:53:09 -08001603 self.assertEqual(f(6, 2), 20)
1604 self.assertEqual(mock_int.__hash__.call_count, 2)
1605 self.assertEqual(f.cache_info(), (1, 2, 1, 1))
Raymond Hettingerd191ef22017-01-07 20:44:48 -08001606
1607 # Cache miss: One use as an argument gives one additional call
Raymond Hettinger5eed36f2017-01-07 20:53:09 -08001608 self.assertEqual(f(mock_int, 1), 16)
1609 self.assertEqual(mock_int.__hash__.call_count, 3)
1610 self.assertEqual(f.cache_info(), (1, 3, 1, 1))
Raymond Hettingerd191ef22017-01-07 20:44:48 -08001611
Raymond Hettingeraf56e0e2016-12-16 13:57:40 -08001612 def test_lru_reentrancy_with_len(self):
1613 # Test to make sure the LRU cache code isn't thrown-off by
1614 # caching the built-in len() function. Since len() can be
1615 # cached, we shouldn't use it inside the lru code itself.
1616 old_len = builtins.len
1617 try:
1618 builtins.len = self.module.lru_cache(4)(len)
1619 for i in [0, 0, 1, 2, 3, 3, 4, 5, 6, 1, 7, 2, 1]:
1620 self.assertEqual(len('abcdefghijklmn'[:i]), i)
1621 finally:
1622 builtins.len = old_len
1623
Raymond Hettinger605a4472017-01-09 07:50:19 -08001624 def test_lru_star_arg_handling(self):
1625 # Test regression that arose in ea064ff3c10f
1626 @functools.lru_cache()
1627 def f(*args):
1628 return args
1629
1630 self.assertEqual(f(1, 2), (1, 2))
1631 self.assertEqual(f((1, 2)), ((1, 2),))
1632
Yury Selivanov46a02db2016-11-09 18:55:45 -05001633 def test_lru_type_error(self):
1634 # Regression test for issue #28653.
1635 # lru_cache was leaking when one of the arguments
1636 # wasn't cacheable.
1637
1638 @functools.lru_cache(maxsize=None)
1639 def infinite_cache(o):
1640 pass
1641
1642 @functools.lru_cache(maxsize=10)
1643 def limited_cache(o):
1644 pass
1645
1646 with self.assertRaises(TypeError):
1647 infinite_cache([])
1648
1649 with self.assertRaises(TypeError):
1650 limited_cache([])
1651
Raymond Hettingerc79fb0e2010-12-01 03:45:41 +00001652 def test_lru_with_maxsize_none(self):
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001653 @self.module.lru_cache(maxsize=None)
Raymond Hettingerc79fb0e2010-12-01 03:45:41 +00001654 def fib(n):
1655 if n < 2:
1656 return n
1657 return fib(n-1) + fib(n-2)
1658 self.assertEqual([fib(n) for n in range(16)],
1659 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
1660 self.assertEqual(fib.cache_info(),
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001661 self.module._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
Raymond Hettingerc79fb0e2010-12-01 03:45:41 +00001662 fib.cache_clear()
1663 self.assertEqual(fib.cache_info(),
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001664 self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
1665
1666 def test_lru_with_maxsize_negative(self):
1667 @self.module.lru_cache(maxsize=-10)
1668 def eq(n):
1669 return n
1670 for i in (0, 1):
1671 self.assertEqual([eq(n) for n in range(150)], list(range(150)))
1672 self.assertEqual(eq.cache_info(),
Raymond Hettingerd8080c02019-01-26 03:02:00 -05001673 self.module._CacheInfo(hits=0, misses=300, maxsize=0, currsize=0))
Raymond Hettingerc79fb0e2010-12-01 03:45:41 +00001674
Raymond Hettinger4b779b32011-10-15 23:50:42 -07001675 def test_lru_with_exceptions(self):
1676 # Verify that user_function exceptions get passed through without
1677 # creating a hard-to-read chained exception.
1678 # http://bugs.python.org/issue13177
Antoine Pitroub5b37142012-11-13 21:35:40 +01001679 for maxsize in (None, 128):
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001680 @self.module.lru_cache(maxsize)
Raymond Hettinger4b779b32011-10-15 23:50:42 -07001681 def func(i):
1682 return 'abc'[i]
1683 self.assertEqual(func(0), 'a')
1684 with self.assertRaises(IndexError) as cm:
1685 func(15)
1686 self.assertIsNone(cm.exception.__context__)
1687 # Verify that the previous exception did not result in a cached entry
1688 with self.assertRaises(IndexError):
1689 func(15)
1690
Raymond Hettingercd9fdfd2011-10-20 08:57:45 -07001691 def test_lru_with_types(self):
Antoine Pitroub5b37142012-11-13 21:35:40 +01001692 for maxsize in (None, 128):
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001693 @self.module.lru_cache(maxsize=maxsize, typed=True)
Raymond Hettingercd9fdfd2011-10-20 08:57:45 -07001694 def square(x):
1695 return x * x
1696 self.assertEqual(square(3), 9)
1697 self.assertEqual(type(square(3)), type(9))
1698 self.assertEqual(square(3.0), 9.0)
1699 self.assertEqual(type(square(3.0)), type(9.0))
1700 self.assertEqual(square(x=3), 9)
1701 self.assertEqual(type(square(x=3)), type(9))
1702 self.assertEqual(square(x=3.0), 9.0)
1703 self.assertEqual(type(square(x=3.0)), type(9.0))
1704 self.assertEqual(square.cache_info().hits, 4)
1705 self.assertEqual(square.cache_info().misses, 4)
1706
Antoine Pitroub5b37142012-11-13 21:35:40 +01001707 def test_lru_with_keyword_args(self):
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001708 @self.module.lru_cache()
Antoine Pitroub5b37142012-11-13 21:35:40 +01001709 def fib(n):
1710 if n < 2:
1711 return n
1712 return fib(n=n-1) + fib(n=n-2)
1713 self.assertEqual(
1714 [fib(n=number) for number in range(16)],
1715 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
1716 )
1717 self.assertEqual(fib.cache_info(),
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001718 self.module._CacheInfo(hits=28, misses=16, maxsize=128, currsize=16))
Antoine Pitroub5b37142012-11-13 21:35:40 +01001719 fib.cache_clear()
1720 self.assertEqual(fib.cache_info(),
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001721 self.module._CacheInfo(hits=0, misses=0, maxsize=128, currsize=0))
Antoine Pitroub5b37142012-11-13 21:35:40 +01001722
1723 def test_lru_with_keyword_args_maxsize_none(self):
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001724 @self.module.lru_cache(maxsize=None)
Antoine Pitroub5b37142012-11-13 21:35:40 +01001725 def fib(n):
1726 if n < 2:
1727 return n
1728 return fib(n=n-1) + fib(n=n-2)
1729 self.assertEqual([fib(n=number) for number in range(16)],
1730 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
1731 self.assertEqual(fib.cache_info(),
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001732 self.module._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
Antoine Pitroub5b37142012-11-13 21:35:40 +01001733 fib.cache_clear()
1734 self.assertEqual(fib.cache_info(),
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001735 self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
1736
Raymond Hettinger4ee39142017-01-08 17:28:20 -08001737 def test_kwargs_order(self):
1738 # PEP 468: Preserving Keyword Argument Order
1739 @self.module.lru_cache(maxsize=10)
1740 def f(**kwargs):
1741 return list(kwargs.items())
1742 self.assertEqual(f(a=1, b=2), [('a', 1), ('b', 2)])
1743 self.assertEqual(f(b=2, a=1), [('b', 2), ('a', 1)])
1744 self.assertEqual(f.cache_info(),
1745 self.module._CacheInfo(hits=0, misses=2, maxsize=10, currsize=2))
1746
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001747 def test_lru_cache_decoration(self):
1748 def f(zomg: 'zomg_annotation'):
1749 """f doc string"""
1750 return 42
1751 g = self.module.lru_cache()(f)
1752 for attr in self.module.WRAPPER_ASSIGNMENTS:
1753 self.assertEqual(getattr(g, attr), getattr(f, attr))
1754
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001755 def test_lru_cache_threaded(self):
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001756 n, m = 5, 11
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001757 def orig(x, y):
1758 return 3 * x + y
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001759 f = self.module.lru_cache(maxsize=n*m)(orig)
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001760 hits, misses, maxsize, currsize = f.cache_info()
1761 self.assertEqual(currsize, 0)
1762
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001763 start = threading.Event()
Serhiy Storchaka391af752015-06-08 12:44:18 +03001764 def full(k):
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001765 start.wait(10)
1766 for _ in range(m):
Serhiy Storchaka391af752015-06-08 12:44:18 +03001767 self.assertEqual(f(k, 0), orig(k, 0))
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001768
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001769 def clear():
1770 start.wait(10)
1771 for _ in range(2*m):
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001772 f.cache_clear()
1773
1774 orig_si = sys.getswitchinterval()
Xavier de Gaye7522ef42016-12-08 11:06:56 +01001775 support.setswitchinterval(1e-6)
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001776 try:
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001777 # create n threads in order to fill cache
Serhiy Storchaka391af752015-06-08 12:44:18 +03001778 threads = [threading.Thread(target=full, args=[k])
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001779 for k in range(n)]
Serhiy Storchakabf2b3b72015-05-30 15:49:17 +03001780 with support.start_threads(threads):
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001781 start.set()
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001782
1783 hits, misses, maxsize, currsize = f.cache_info()
Serhiy Storchaka391af752015-06-08 12:44:18 +03001784 if self.module is py_functools:
1785 # XXX: Why can be not equal?
1786 self.assertLessEqual(misses, n)
1787 self.assertLessEqual(hits, m*n - misses)
1788 else:
1789 self.assertEqual(misses, n)
1790 self.assertEqual(hits, m*n - misses)
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001791 self.assertEqual(currsize, n)
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001792
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001793 # create n threads in order to fill cache and 1 to clear it
1794 threads = [threading.Thread(target=clear)]
Serhiy Storchaka391af752015-06-08 12:44:18 +03001795 threads += [threading.Thread(target=full, args=[k])
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001796 for k in range(n)]
1797 start.clear()
Serhiy Storchakabf2b3b72015-05-30 15:49:17 +03001798 with support.start_threads(threads):
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001799 start.set()
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001800 finally:
1801 sys.setswitchinterval(orig_si)
Antoine Pitroub5b37142012-11-13 21:35:40 +01001802
Serhiy Storchaka77cb1972015-06-08 11:14:31 +03001803 def test_lru_cache_threaded2(self):
1804 # Simultaneous call with the same arguments
1805 n, m = 5, 7
1806 start = threading.Barrier(n+1)
1807 pause = threading.Barrier(n+1)
1808 stop = threading.Barrier(n+1)
1809 @self.module.lru_cache(maxsize=m*n)
1810 def f(x):
1811 pause.wait(10)
1812 return 3 * x
1813 self.assertEqual(f.cache_info(), (0, 0, m*n, 0))
1814 def test():
1815 for i in range(m):
1816 start.wait(10)
1817 self.assertEqual(f(i), 3 * i)
1818 stop.wait(10)
1819 threads = [threading.Thread(target=test) for k in range(n)]
1820 with support.start_threads(threads):
1821 for i in range(m):
1822 start.wait(10)
1823 stop.reset()
1824 pause.wait(10)
1825 start.reset()
1826 stop.wait(10)
1827 pause.reset()
1828 self.assertEqual(f.cache_info(), (0, (i+1)*n, m*n, i+1))
1829
Serhiy Storchaka67796522017-01-12 18:34:33 +02001830 def test_lru_cache_threaded3(self):
1831 @self.module.lru_cache(maxsize=2)
1832 def f(x):
1833 time.sleep(.01)
1834 return 3 * x
1835 def test(i, x):
1836 with self.subTest(thread=i):
1837 self.assertEqual(f(x), 3 * x, i)
1838 threads = [threading.Thread(target=test, args=(i, v))
1839 for i, v in enumerate([1, 2, 2, 3, 2])]
1840 with support.start_threads(threads):
1841 pass
1842
Raymond Hettinger03923422013-03-04 02:52:50 -05001843 def test_need_for_rlock(self):
1844 # This will deadlock on an LRU cache that uses a regular lock
1845
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001846 @self.module.lru_cache(maxsize=10)
Raymond Hettinger03923422013-03-04 02:52:50 -05001847 def test_func(x):
1848 'Used to demonstrate a reentrant lru_cache call within a single thread'
1849 return x
1850
1851 class DoubleEq:
1852 'Demonstrate a reentrant lru_cache call within a single thread'
1853 def __init__(self, x):
1854 self.x = x
1855 def __hash__(self):
1856 return self.x
1857 def __eq__(self, other):
1858 if self.x == 2:
1859 test_func(DoubleEq(1))
1860 return self.x == other.x
1861
1862 test_func(DoubleEq(1)) # Load the cache
1863 test_func(DoubleEq(2)) # Load the cache
1864 self.assertEqual(test_func(DoubleEq(2)), # Trigger a re-entrant __eq__ call
1865 DoubleEq(2)) # Verify the correct return value
1866
Serhiy Storchakae7070f02015-06-08 11:19:24 +03001867 def test_lru_method(self):
1868 class X(int):
1869 f_cnt = 0
1870 @self.module.lru_cache(2)
1871 def f(self, x):
1872 self.f_cnt += 1
1873 return x*10+self
1874 a = X(5)
1875 b = X(5)
1876 c = X(7)
1877 self.assertEqual(X.f.cache_info(), (0, 0, 2, 0))
1878
1879 for x in 1, 2, 2, 3, 1, 1, 1, 2, 3, 3:
1880 self.assertEqual(a.f(x), x*10 + 5)
1881 self.assertEqual((a.f_cnt, b.f_cnt, c.f_cnt), (6, 0, 0))
1882 self.assertEqual(X.f.cache_info(), (4, 6, 2, 2))
1883
1884 for x in 1, 2, 1, 1, 1, 1, 3, 2, 2, 2:
1885 self.assertEqual(b.f(x), x*10 + 5)
1886 self.assertEqual((a.f_cnt, b.f_cnt, c.f_cnt), (6, 4, 0))
1887 self.assertEqual(X.f.cache_info(), (10, 10, 2, 2))
1888
1889 for x in 2, 1, 1, 1, 1, 2, 1, 3, 2, 1:
1890 self.assertEqual(c.f(x), x*10 + 7)
1891 self.assertEqual((a.f_cnt, b.f_cnt, c.f_cnt), (6, 4, 5))
1892 self.assertEqual(X.f.cache_info(), (15, 15, 2, 2))
1893
1894 self.assertEqual(a.f.cache_info(), X.f.cache_info())
1895 self.assertEqual(b.f.cache_info(), X.f.cache_info())
1896 self.assertEqual(c.f.cache_info(), X.f.cache_info())
1897
Serhiy Storchaka45120f22015-10-24 09:49:56 +03001898 def test_pickle(self):
1899 cls = self.__class__
1900 for f in cls.cached_func[0], cls.cached_meth, cls.cached_staticmeth:
1901 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1902 with self.subTest(proto=proto, func=f):
1903 f_copy = pickle.loads(pickle.dumps(f, proto))
1904 self.assertIs(f_copy, f)
1905
1906 def test_copy(self):
1907 cls = self.__class__
Serhiy Storchakae4d65e32015-12-28 23:58:07 +02001908 def orig(x, y):
1909 return 3 * x + y
1910 part = self.module.partial(orig, 2)
1911 funcs = (cls.cached_func[0], cls.cached_meth, cls.cached_staticmeth,
1912 self.module.lru_cache(2)(part))
1913 for f in funcs:
Serhiy Storchaka45120f22015-10-24 09:49:56 +03001914 with self.subTest(func=f):
1915 f_copy = copy.copy(f)
1916 self.assertIs(f_copy, f)
1917
1918 def test_deepcopy(self):
1919 cls = self.__class__
Serhiy Storchakae4d65e32015-12-28 23:58:07 +02001920 def orig(x, y):
1921 return 3 * x + y
1922 part = self.module.partial(orig, 2)
1923 funcs = (cls.cached_func[0], cls.cached_meth, cls.cached_staticmeth,
1924 self.module.lru_cache(2)(part))
1925 for f in funcs:
Serhiy Storchaka45120f22015-10-24 09:49:56 +03001926 with self.subTest(func=f):
1927 f_copy = copy.deepcopy(f)
1928 self.assertIs(f_copy, f)
1929
Manjusaka051ff522019-11-12 15:30:18 +08001930 def test_lru_cache_parameters(self):
1931 @self.module.lru_cache(maxsize=2)
1932 def f():
1933 return 1
1934 self.assertEqual(f.cache_parameters(), {'maxsize': 2, "typed": False})
1935
1936 @self.module.lru_cache(maxsize=1000, typed=True)
1937 def f():
1938 return 1
1939 self.assertEqual(f.cache_parameters(), {'maxsize': 1000, "typed": True})
1940
Serhiy Storchaka45120f22015-10-24 09:49:56 +03001941
1942@py_functools.lru_cache()
1943def py_cached_func(x, y):
1944 return 3 * x + y
1945
1946@c_functools.lru_cache()
1947def c_cached_func(x, y):
1948 return 3 * x + y
1949
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001950
1951class TestLRUPy(TestLRU, unittest.TestCase):
1952 module = py_functools
Serhiy Storchaka45120f22015-10-24 09:49:56 +03001953 cached_func = py_cached_func,
1954
1955 @module.lru_cache()
1956 def cached_meth(self, x, y):
1957 return 3 * x + y
1958
1959 @staticmethod
1960 @module.lru_cache()
1961 def cached_staticmeth(x, y):
1962 return 3 * x + y
1963
1964
1965class TestLRUC(TestLRU, unittest.TestCase):
1966 module = c_functools
1967 cached_func = c_cached_func,
1968
1969 @module.lru_cache()
1970 def cached_meth(self, x, y):
1971 return 3 * x + y
1972
1973 @staticmethod
1974 @module.lru_cache()
1975 def cached_staticmeth(x, y):
1976 return 3 * x + y
Serhiy Storchaka46c56112015-05-24 21:53:49 +03001977
Raymond Hettinger03923422013-03-04 02:52:50 -05001978
Łukasz Langa6f692512013-06-05 12:20:24 +02001979class TestSingleDispatch(unittest.TestCase):
1980 def test_simple_overloads(self):
1981 @functools.singledispatch
1982 def g(obj):
1983 return "base"
1984 def g_int(i):
1985 return "integer"
1986 g.register(int, g_int)
1987 self.assertEqual(g("str"), "base")
1988 self.assertEqual(g(1), "integer")
1989 self.assertEqual(g([1,2,3]), "base")
1990
1991 def test_mro(self):
1992 @functools.singledispatch
1993 def g(obj):
1994 return "base"
Łukasz Langa7f7a67a2013-06-07 22:25:27 +02001995 class A:
Łukasz Langa6f692512013-06-05 12:20:24 +02001996 pass
Łukasz Langa7f7a67a2013-06-07 22:25:27 +02001997 class C(A):
Łukasz Langa6f692512013-06-05 12:20:24 +02001998 pass
Łukasz Langa7f7a67a2013-06-07 22:25:27 +02001999 class B(A):
Łukasz Langa6f692512013-06-05 12:20:24 +02002000 pass
Łukasz Langa7f7a67a2013-06-07 22:25:27 +02002001 class D(C, B):
Łukasz Langa6f692512013-06-05 12:20:24 +02002002 pass
Łukasz Langa7f7a67a2013-06-07 22:25:27 +02002003 def g_A(a):
2004 return "A"
2005 def g_B(b):
2006 return "B"
2007 g.register(A, g_A)
2008 g.register(B, g_B)
2009 self.assertEqual(g(A()), "A")
2010 self.assertEqual(g(B()), "B")
2011 self.assertEqual(g(C()), "A")
2012 self.assertEqual(g(D()), "B")
Łukasz Langa6f692512013-06-05 12:20:24 +02002013
2014 def test_register_decorator(self):
2015 @functools.singledispatch
2016 def g(obj):
2017 return "base"
2018 @g.register(int)
2019 def g_int(i):
2020 return "int %s" % (i,)
2021 self.assertEqual(g(""), "base")
2022 self.assertEqual(g(12), "int 12")
2023 self.assertIs(g.dispatch(int), g_int)
2024 self.assertIs(g.dispatch(object), g.dispatch(str))
2025 # Note: in the assert above this is not g.
2026 # @singledispatch returns the wrapper.
2027
2028 def test_wrapping_attributes(self):
2029 @functools.singledispatch
2030 def g(obj):
2031 "Simple test"
2032 return "Test"
2033 self.assertEqual(g.__name__, "g")
Serhiy Storchakab12cb6a2013-12-08 18:16:18 +02002034 if sys.flags.optimize < 2:
2035 self.assertEqual(g.__doc__, "Simple test")
Łukasz Langa6f692512013-06-05 12:20:24 +02002036
2037 @unittest.skipUnless(decimal, 'requires _decimal')
2038 @support.cpython_only
2039 def test_c_classes(self):
2040 @functools.singledispatch
2041 def g(obj):
2042 return "base"
2043 @g.register(decimal.DecimalException)
2044 def _(obj):
2045 return obj.args
2046 subn = decimal.Subnormal("Exponent < Emin")
2047 rnd = decimal.Rounded("Number got rounded")
2048 self.assertEqual(g(subn), ("Exponent < Emin",))
2049 self.assertEqual(g(rnd), ("Number got rounded",))
2050 @g.register(decimal.Subnormal)
2051 def _(obj):
2052 return "Too small to care."
2053 self.assertEqual(g(subn), "Too small to care.")
2054 self.assertEqual(g(rnd), ("Number got rounded",))
2055
2056 def test_compose_mro(self):
Łukasz Langa3720c772013-07-01 16:00:38 +02002057 # None of the examples in this test depend on haystack ordering.
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002058 c = collections.abc
Łukasz Langa6f692512013-06-05 12:20:24 +02002059 mro = functools._compose_mro
2060 bases = [c.Sequence, c.MutableMapping, c.Mapping, c.Set]
2061 for haystack in permutations(bases):
2062 m = mro(dict, haystack)
Guido van Rossumf0666942016-08-23 10:47:07 -07002063 self.assertEqual(m, [dict, c.MutableMapping, c.Mapping,
2064 c.Collection, c.Sized, c.Iterable,
2065 c.Container, object])
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002066 bases = [c.Container, c.Mapping, c.MutableMapping, collections.OrderedDict]
Łukasz Langa6f692512013-06-05 12:20:24 +02002067 for haystack in permutations(bases):
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002068 m = mro(collections.ChainMap, haystack)
2069 self.assertEqual(m, [collections.ChainMap, c.MutableMapping, c.Mapping,
Guido van Rossumf0666942016-08-23 10:47:07 -07002070 c.Collection, c.Sized, c.Iterable,
2071 c.Container, object])
Łukasz Langa3720c772013-07-01 16:00:38 +02002072
2073 # If there's a generic function with implementations registered for
2074 # both Sized and Container, passing a defaultdict to it results in an
2075 # ambiguous dispatch which will cause a RuntimeError (see
2076 # test_mro_conflicts).
2077 bases = [c.Container, c.Sized, str]
2078 for haystack in permutations(bases):
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002079 m = mro(collections.defaultdict, [c.Sized, c.Container, str])
2080 self.assertEqual(m, [collections.defaultdict, dict, c.Sized,
2081 c.Container, object])
Łukasz Langa3720c772013-07-01 16:00:38 +02002082
2083 # MutableSequence below is registered directly on D. In other words, it
Martin Panter46f50722016-05-26 05:35:26 +00002084 # precedes MutableMapping which means single dispatch will always
Łukasz Langa3720c772013-07-01 16:00:38 +02002085 # choose MutableSequence here.
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002086 class D(collections.defaultdict):
Łukasz Langa3720c772013-07-01 16:00:38 +02002087 pass
2088 c.MutableSequence.register(D)
2089 bases = [c.MutableSequence, c.MutableMapping]
2090 for haystack in permutations(bases):
2091 m = mro(D, bases)
Guido van Rossumf0666942016-08-23 10:47:07 -07002092 self.assertEqual(m, [D, c.MutableSequence, c.Sequence, c.Reversible,
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002093 collections.defaultdict, dict, c.MutableMapping, c.Mapping,
Guido van Rossumf0666942016-08-23 10:47:07 -07002094 c.Collection, c.Sized, c.Iterable, c.Container,
Łukasz Langa3720c772013-07-01 16:00:38 +02002095 object])
2096
2097 # Container and Callable are registered on different base classes and
2098 # a generic function supporting both should always pick the Callable
2099 # implementation if a C instance is passed.
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002100 class C(collections.defaultdict):
Łukasz Langa3720c772013-07-01 16:00:38 +02002101 def __call__(self):
2102 pass
2103 bases = [c.Sized, c.Callable, c.Container, c.Mapping]
2104 for haystack in permutations(bases):
2105 m = mro(C, haystack)
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002106 self.assertEqual(m, [C, c.Callable, collections.defaultdict, dict, c.Mapping,
Guido van Rossumf0666942016-08-23 10:47:07 -07002107 c.Collection, c.Sized, c.Iterable,
2108 c.Container, object])
Łukasz Langa6f692512013-06-05 12:20:24 +02002109
2110 def test_register_abc(self):
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002111 c = collections.abc
Łukasz Langa6f692512013-06-05 12:20:24 +02002112 d = {"a": "b"}
2113 l = [1, 2, 3]
2114 s = {object(), None}
2115 f = frozenset(s)
2116 t = (1, 2, 3)
2117 @functools.singledispatch
2118 def g(obj):
2119 return "base"
2120 self.assertEqual(g(d), "base")
2121 self.assertEqual(g(l), "base")
2122 self.assertEqual(g(s), "base")
2123 self.assertEqual(g(f), "base")
2124 self.assertEqual(g(t), "base")
2125 g.register(c.Sized, lambda obj: "sized")
2126 self.assertEqual(g(d), "sized")
2127 self.assertEqual(g(l), "sized")
2128 self.assertEqual(g(s), "sized")
2129 self.assertEqual(g(f), "sized")
2130 self.assertEqual(g(t), "sized")
2131 g.register(c.MutableMapping, lambda obj: "mutablemapping")
2132 self.assertEqual(g(d), "mutablemapping")
2133 self.assertEqual(g(l), "sized")
2134 self.assertEqual(g(s), "sized")
2135 self.assertEqual(g(f), "sized")
2136 self.assertEqual(g(t), "sized")
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002137 g.register(collections.ChainMap, lambda obj: "chainmap")
Łukasz Langa6f692512013-06-05 12:20:24 +02002138 self.assertEqual(g(d), "mutablemapping") # irrelevant ABCs registered
2139 self.assertEqual(g(l), "sized")
2140 self.assertEqual(g(s), "sized")
2141 self.assertEqual(g(f), "sized")
2142 self.assertEqual(g(t), "sized")
2143 g.register(c.MutableSequence, lambda obj: "mutablesequence")
2144 self.assertEqual(g(d), "mutablemapping")
2145 self.assertEqual(g(l), "mutablesequence")
2146 self.assertEqual(g(s), "sized")
2147 self.assertEqual(g(f), "sized")
2148 self.assertEqual(g(t), "sized")
2149 g.register(c.MutableSet, lambda obj: "mutableset")
2150 self.assertEqual(g(d), "mutablemapping")
2151 self.assertEqual(g(l), "mutablesequence")
2152 self.assertEqual(g(s), "mutableset")
2153 self.assertEqual(g(f), "sized")
2154 self.assertEqual(g(t), "sized")
2155 g.register(c.Mapping, lambda obj: "mapping")
2156 self.assertEqual(g(d), "mutablemapping") # not specific enough
2157 self.assertEqual(g(l), "mutablesequence")
2158 self.assertEqual(g(s), "mutableset")
2159 self.assertEqual(g(f), "sized")
2160 self.assertEqual(g(t), "sized")
2161 g.register(c.Sequence, lambda obj: "sequence")
2162 self.assertEqual(g(d), "mutablemapping")
2163 self.assertEqual(g(l), "mutablesequence")
2164 self.assertEqual(g(s), "mutableset")
2165 self.assertEqual(g(f), "sized")
2166 self.assertEqual(g(t), "sequence")
2167 g.register(c.Set, lambda obj: "set")
2168 self.assertEqual(g(d), "mutablemapping")
2169 self.assertEqual(g(l), "mutablesequence")
2170 self.assertEqual(g(s), "mutableset")
2171 self.assertEqual(g(f), "set")
2172 self.assertEqual(g(t), "sequence")
2173 g.register(dict, lambda obj: "dict")
2174 self.assertEqual(g(d), "dict")
2175 self.assertEqual(g(l), "mutablesequence")
2176 self.assertEqual(g(s), "mutableset")
2177 self.assertEqual(g(f), "set")
2178 self.assertEqual(g(t), "sequence")
2179 g.register(list, lambda obj: "list")
2180 self.assertEqual(g(d), "dict")
2181 self.assertEqual(g(l), "list")
2182 self.assertEqual(g(s), "mutableset")
2183 self.assertEqual(g(f), "set")
2184 self.assertEqual(g(t), "sequence")
2185 g.register(set, lambda obj: "concrete-set")
2186 self.assertEqual(g(d), "dict")
2187 self.assertEqual(g(l), "list")
2188 self.assertEqual(g(s), "concrete-set")
2189 self.assertEqual(g(f), "set")
2190 self.assertEqual(g(t), "sequence")
2191 g.register(frozenset, lambda obj: "frozen-set")
2192 self.assertEqual(g(d), "dict")
2193 self.assertEqual(g(l), "list")
2194 self.assertEqual(g(s), "concrete-set")
2195 self.assertEqual(g(f), "frozen-set")
2196 self.assertEqual(g(t), "sequence")
2197 g.register(tuple, lambda obj: "tuple")
2198 self.assertEqual(g(d), "dict")
2199 self.assertEqual(g(l), "list")
2200 self.assertEqual(g(s), "concrete-set")
2201 self.assertEqual(g(f), "frozen-set")
2202 self.assertEqual(g(t), "tuple")
2203
Łukasz Langa3720c772013-07-01 16:00:38 +02002204 def test_c3_abc(self):
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002205 c = collections.abc
Łukasz Langa3720c772013-07-01 16:00:38 +02002206 mro = functools._c3_mro
2207 class A(object):
2208 pass
2209 class B(A):
2210 def __len__(self):
2211 return 0 # implies Sized
2212 @c.Container.register
2213 class C(object):
2214 pass
2215 class D(object):
2216 pass # unrelated
2217 class X(D, C, B):
2218 def __call__(self):
2219 pass # implies Callable
2220 expected = [X, c.Callable, D, C, c.Container, B, c.Sized, A, object]
2221 for abcs in permutations([c.Sized, c.Callable, c.Container]):
2222 self.assertEqual(mro(X, abcs=abcs), expected)
2223 # unrelated ABCs don't appear in the resulting MRO
2224 many_abcs = [c.Mapping, c.Sized, c.Callable, c.Container, c.Iterable]
2225 self.assertEqual(mro(X, abcs=many_abcs), expected)
2226
Yury Selivanov77a8cd62015-08-18 14:20:00 -04002227 def test_false_meta(self):
2228 # see issue23572
2229 class MetaA(type):
2230 def __len__(self):
2231 return 0
2232 class A(metaclass=MetaA):
2233 pass
2234 class AA(A):
2235 pass
2236 @functools.singledispatch
2237 def fun(a):
2238 return 'base A'
2239 @fun.register(A)
2240 def _(a):
2241 return 'fun A'
2242 aa = AA()
2243 self.assertEqual(fun(aa), 'fun A')
2244
Łukasz Langa6f692512013-06-05 12:20:24 +02002245 def test_mro_conflicts(self):
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002246 c = collections.abc
Łukasz Langa6f692512013-06-05 12:20:24 +02002247 @functools.singledispatch
2248 def g(arg):
2249 return "base"
Łukasz Langa6f692512013-06-05 12:20:24 +02002250 class O(c.Sized):
2251 def __len__(self):
2252 return 0
Łukasz Langa6f692512013-06-05 12:20:24 +02002253 o = O()
2254 self.assertEqual(g(o), "base")
2255 g.register(c.Iterable, lambda arg: "iterable")
2256 g.register(c.Container, lambda arg: "container")
2257 g.register(c.Sized, lambda arg: "sized")
2258 g.register(c.Set, lambda arg: "set")
2259 self.assertEqual(g(o), "sized")
2260 c.Iterable.register(O)
2261 self.assertEqual(g(o), "sized") # because it's explicitly in __mro__
2262 c.Container.register(O)
2263 self.assertEqual(g(o), "sized") # see above: Sized is in __mro__
Łukasz Langa3720c772013-07-01 16:00:38 +02002264 c.Set.register(O)
2265 self.assertEqual(g(o), "set") # because c.Set is a subclass of
2266 # c.Sized and c.Container
Łukasz Langa6f692512013-06-05 12:20:24 +02002267 class P:
2268 pass
Łukasz Langa6f692512013-06-05 12:20:24 +02002269 p = P()
2270 self.assertEqual(g(p), "base")
2271 c.Iterable.register(P)
2272 self.assertEqual(g(p), "iterable")
2273 c.Container.register(P)
Łukasz Langa3720c772013-07-01 16:00:38 +02002274 with self.assertRaises(RuntimeError) as re_one:
Łukasz Langa6f692512013-06-05 12:20:24 +02002275 g(p)
Benjamin Petersonab078e92016-07-13 21:13:29 -07002276 self.assertIn(
2277 str(re_one.exception),
2278 (("Ambiguous dispatch: <class 'collections.abc.Container'> "
2279 "or <class 'collections.abc.Iterable'>"),
2280 ("Ambiguous dispatch: <class 'collections.abc.Iterable'> "
2281 "or <class 'collections.abc.Container'>")),
2282 )
Łukasz Langa6f692512013-06-05 12:20:24 +02002283 class Q(c.Sized):
2284 def __len__(self):
2285 return 0
Łukasz Langa6f692512013-06-05 12:20:24 +02002286 q = Q()
2287 self.assertEqual(g(q), "sized")
2288 c.Iterable.register(Q)
2289 self.assertEqual(g(q), "sized") # because it's explicitly in __mro__
2290 c.Set.register(Q)
2291 self.assertEqual(g(q), "set") # because c.Set is a subclass of
Łukasz Langa3720c772013-07-01 16:00:38 +02002292 # c.Sized and c.Iterable
2293 @functools.singledispatch
2294 def h(arg):
2295 return "base"
2296 @h.register(c.Sized)
2297 def _(arg):
2298 return "sized"
2299 @h.register(c.Container)
2300 def _(arg):
2301 return "container"
2302 # Even though Sized and Container are explicit bases of MutableMapping,
2303 # this ABC is implicitly registered on defaultdict which makes all of
2304 # MutableMapping's bases implicit as well from defaultdict's
2305 # perspective.
2306 with self.assertRaises(RuntimeError) as re_two:
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002307 h(collections.defaultdict(lambda: 0))
Benjamin Petersonab078e92016-07-13 21:13:29 -07002308 self.assertIn(
2309 str(re_two.exception),
2310 (("Ambiguous dispatch: <class 'collections.abc.Container'> "
2311 "or <class 'collections.abc.Sized'>"),
2312 ("Ambiguous dispatch: <class 'collections.abc.Sized'> "
2313 "or <class 'collections.abc.Container'>")),
2314 )
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03002315 class R(collections.defaultdict):
Łukasz Langa3720c772013-07-01 16:00:38 +02002316 pass
2317 c.MutableSequence.register(R)
2318 @functools.singledispatch
2319 def i(arg):
2320 return "base"
2321 @i.register(c.MutableMapping)
2322 def _(arg):
2323 return "mapping"
2324 @i.register(c.MutableSequence)
2325 def _(arg):
2326 return "sequence"
2327 r = R()
2328 self.assertEqual(i(r), "sequence")
2329 class S:
2330 pass
2331 class T(S, c.Sized):
2332 def __len__(self):
2333 return 0
2334 t = T()
2335 self.assertEqual(h(t), "sized")
2336 c.Container.register(T)
2337 self.assertEqual(h(t), "sized") # because it's explicitly in the MRO
2338 class U:
2339 def __len__(self):
2340 return 0
2341 u = U()
2342 self.assertEqual(h(u), "sized") # implicit Sized subclass inferred
2343 # from the existence of __len__()
2344 c.Container.register(U)
2345 # There is no preference for registered versus inferred ABCs.
2346 with self.assertRaises(RuntimeError) as re_three:
2347 h(u)
Benjamin Petersonab078e92016-07-13 21:13:29 -07002348 self.assertIn(
2349 str(re_three.exception),
2350 (("Ambiguous dispatch: <class 'collections.abc.Container'> "
2351 "or <class 'collections.abc.Sized'>"),
2352 ("Ambiguous dispatch: <class 'collections.abc.Sized'> "
2353 "or <class 'collections.abc.Container'>")),
2354 )
Łukasz Langa3720c772013-07-01 16:00:38 +02002355 class V(c.Sized, S):
2356 def __len__(self):
2357 return 0
2358 @functools.singledispatch
2359 def j(arg):
2360 return "base"
2361 @j.register(S)
2362 def _(arg):
2363 return "s"
2364 @j.register(c.Container)
2365 def _(arg):
2366 return "container"
2367 v = V()
2368 self.assertEqual(j(v), "s")
2369 c.Container.register(V)
2370 self.assertEqual(j(v), "container") # because it ends up right after
2371 # Sized in the MRO
Łukasz Langa6f692512013-06-05 12:20:24 +02002372
2373 def test_cache_invalidation(self):
2374 from collections import UserDict
INADA Naoki9811e802017-09-30 16:13:02 +09002375 import weakref
2376
Łukasz Langa6f692512013-06-05 12:20:24 +02002377 class TracingDict(UserDict):
2378 def __init__(self, *args, **kwargs):
2379 super(TracingDict, self).__init__(*args, **kwargs)
2380 self.set_ops = []
2381 self.get_ops = []
2382 def __getitem__(self, key):
2383 result = self.data[key]
2384 self.get_ops.append(key)
2385 return result
2386 def __setitem__(self, key, value):
2387 self.set_ops.append(key)
2388 self.data[key] = value
2389 def clear(self):
2390 self.data.clear()
INADA Naoki9811e802017-09-30 16:13:02 +09002391
Łukasz Langa6f692512013-06-05 12:20:24 +02002392 td = TracingDict()
INADA Naoki9811e802017-09-30 16:13:02 +09002393 with support.swap_attr(weakref, "WeakKeyDictionary", lambda: td):
2394 c = collections.abc
2395 @functools.singledispatch
2396 def g(arg):
2397 return "base"
2398 d = {}
2399 l = []
2400 self.assertEqual(len(td), 0)
2401 self.assertEqual(g(d), "base")
2402 self.assertEqual(len(td), 1)
2403 self.assertEqual(td.get_ops, [])
2404 self.assertEqual(td.set_ops, [dict])
2405 self.assertEqual(td.data[dict], g.registry[object])
2406 self.assertEqual(g(l), "base")
2407 self.assertEqual(len(td), 2)
2408 self.assertEqual(td.get_ops, [])
2409 self.assertEqual(td.set_ops, [dict, list])
2410 self.assertEqual(td.data[dict], g.registry[object])
2411 self.assertEqual(td.data[list], g.registry[object])
2412 self.assertEqual(td.data[dict], td.data[list])
2413 self.assertEqual(g(l), "base")
2414 self.assertEqual(g(d), "base")
2415 self.assertEqual(td.get_ops, [list, dict])
2416 self.assertEqual(td.set_ops, [dict, list])
2417 g.register(list, lambda arg: "list")
2418 self.assertEqual(td.get_ops, [list, dict])
2419 self.assertEqual(len(td), 0)
2420 self.assertEqual(g(d), "base")
2421 self.assertEqual(len(td), 1)
2422 self.assertEqual(td.get_ops, [list, dict])
2423 self.assertEqual(td.set_ops, [dict, list, dict])
2424 self.assertEqual(td.data[dict],
2425 functools._find_impl(dict, g.registry))
2426 self.assertEqual(g(l), "list")
2427 self.assertEqual(len(td), 2)
2428 self.assertEqual(td.get_ops, [list, dict])
2429 self.assertEqual(td.set_ops, [dict, list, dict, list])
2430 self.assertEqual(td.data[list],
2431 functools._find_impl(list, g.registry))
2432 class X:
2433 pass
2434 c.MutableMapping.register(X) # Will not invalidate the cache,
2435 # not using ABCs yet.
2436 self.assertEqual(g(d), "base")
2437 self.assertEqual(g(l), "list")
2438 self.assertEqual(td.get_ops, [list, dict, dict, list])
2439 self.assertEqual(td.set_ops, [dict, list, dict, list])
2440 g.register(c.Sized, lambda arg: "sized")
2441 self.assertEqual(len(td), 0)
2442 self.assertEqual(g(d), "sized")
2443 self.assertEqual(len(td), 1)
2444 self.assertEqual(td.get_ops, [list, dict, dict, list])
2445 self.assertEqual(td.set_ops, [dict, list, dict, list, dict])
2446 self.assertEqual(g(l), "list")
2447 self.assertEqual(len(td), 2)
2448 self.assertEqual(td.get_ops, [list, dict, dict, list])
2449 self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2450 self.assertEqual(g(l), "list")
2451 self.assertEqual(g(d), "sized")
2452 self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict])
2453 self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2454 g.dispatch(list)
2455 g.dispatch(dict)
2456 self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict,
2457 list, dict])
2458 self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2459 c.MutableSet.register(X) # Will invalidate the cache.
2460 self.assertEqual(len(td), 2) # Stale cache.
2461 self.assertEqual(g(l), "list")
2462 self.assertEqual(len(td), 1)
2463 g.register(c.MutableMapping, lambda arg: "mutablemapping")
2464 self.assertEqual(len(td), 0)
2465 self.assertEqual(g(d), "mutablemapping")
2466 self.assertEqual(len(td), 1)
2467 self.assertEqual(g(l), "list")
2468 self.assertEqual(len(td), 2)
2469 g.register(dict, lambda arg: "dict")
2470 self.assertEqual(g(d), "dict")
2471 self.assertEqual(g(l), "list")
2472 g._clear_cache()
2473 self.assertEqual(len(td), 0)
Łukasz Langa6f692512013-06-05 12:20:24 +02002474
Łukasz Langae5697532017-12-11 13:56:31 -08002475 def test_annotations(self):
2476 @functools.singledispatch
2477 def i(arg):
2478 return "base"
2479 @i.register
2480 def _(arg: collections.abc.Mapping):
2481 return "mapping"
2482 @i.register
2483 def _(arg: "collections.abc.Sequence"):
2484 return "sequence"
2485 self.assertEqual(i(None), "base")
2486 self.assertEqual(i({"a": 1}), "mapping")
2487 self.assertEqual(i([1, 2, 3]), "sequence")
2488 self.assertEqual(i((1, 2, 3)), "sequence")
2489 self.assertEqual(i("str"), "sequence")
2490
2491 # Registering classes as callables doesn't work with annotations,
2492 # you need to pass the type explicitly.
2493 @i.register(str)
2494 class _:
2495 def __init__(self, arg):
2496 self.arg = arg
2497
2498 def __eq__(self, other):
2499 return self.arg == other
2500 self.assertEqual(i("str"), "str")
2501
Ethan Smithc6512752018-05-26 16:38:33 -04002502 def test_method_register(self):
2503 class A:
2504 @functools.singledispatchmethod
2505 def t(self, arg):
2506 self.arg = "base"
2507 @t.register(int)
2508 def _(self, arg):
2509 self.arg = "int"
2510 @t.register(str)
2511 def _(self, arg):
2512 self.arg = "str"
2513 a = A()
2514
2515 a.t(0)
2516 self.assertEqual(a.arg, "int")
2517 aa = A()
2518 self.assertFalse(hasattr(aa, 'arg'))
2519 a.t('')
2520 self.assertEqual(a.arg, "str")
2521 aa = A()
2522 self.assertFalse(hasattr(aa, 'arg'))
2523 a.t(0.0)
2524 self.assertEqual(a.arg, "base")
2525 aa = A()
2526 self.assertFalse(hasattr(aa, 'arg'))
2527
2528 def test_staticmethod_register(self):
2529 class A:
2530 @functools.singledispatchmethod
2531 @staticmethod
2532 def t(arg):
2533 return arg
2534 @t.register(int)
2535 @staticmethod
2536 def _(arg):
2537 return isinstance(arg, int)
2538 @t.register(str)
2539 @staticmethod
2540 def _(arg):
2541 return isinstance(arg, str)
2542 a = A()
2543
2544 self.assertTrue(A.t(0))
2545 self.assertTrue(A.t(''))
2546 self.assertEqual(A.t(0.0), 0.0)
2547
2548 def test_classmethod_register(self):
2549 class A:
2550 def __init__(self, arg):
2551 self.arg = arg
2552
2553 @functools.singledispatchmethod
2554 @classmethod
2555 def t(cls, arg):
2556 return cls("base")
2557 @t.register(int)
2558 @classmethod
2559 def _(cls, arg):
2560 return cls("int")
2561 @t.register(str)
2562 @classmethod
2563 def _(cls, arg):
2564 return cls("str")
2565
2566 self.assertEqual(A.t(0).arg, "int")
2567 self.assertEqual(A.t('').arg, "str")
2568 self.assertEqual(A.t(0.0).arg, "base")
2569
2570 def test_callable_register(self):
2571 class A:
2572 def __init__(self, arg):
2573 self.arg = arg
2574
2575 @functools.singledispatchmethod
2576 @classmethod
2577 def t(cls, arg):
2578 return cls("base")
2579
2580 @A.t.register(int)
2581 @classmethod
2582 def _(cls, arg):
2583 return cls("int")
2584 @A.t.register(str)
2585 @classmethod
2586 def _(cls, arg):
2587 return cls("str")
2588
2589 self.assertEqual(A.t(0).arg, "int")
2590 self.assertEqual(A.t('').arg, "str")
2591 self.assertEqual(A.t(0.0).arg, "base")
2592
2593 def test_abstractmethod_register(self):
2594 class Abstract(abc.ABCMeta):
2595
2596 @functools.singledispatchmethod
2597 @abc.abstractmethod
2598 def add(self, x, y):
2599 pass
2600
2601 self.assertTrue(Abstract.add.__isabstractmethod__)
2602
2603 def test_type_ann_register(self):
2604 class A:
2605 @functools.singledispatchmethod
2606 def t(self, arg):
2607 return "base"
2608 @t.register
2609 def _(self, arg: int):
2610 return "int"
2611 @t.register
2612 def _(self, arg: str):
2613 return "str"
2614 a = A()
2615
2616 self.assertEqual(a.t(0), "int")
2617 self.assertEqual(a.t(''), "str")
2618 self.assertEqual(a.t(0.0), "base")
2619
Łukasz Langae5697532017-12-11 13:56:31 -08002620 def test_invalid_registrations(self):
2621 msg_prefix = "Invalid first argument to `register()`: "
2622 msg_suffix = (
2623 ". Use either `@register(some_class)` or plain `@register` on an "
2624 "annotated function."
2625 )
2626 @functools.singledispatch
2627 def i(arg):
2628 return "base"
2629 with self.assertRaises(TypeError) as exc:
2630 @i.register(42)
2631 def _(arg):
2632 return "I annotated with a non-type"
2633 self.assertTrue(str(exc.exception).startswith(msg_prefix + "42"))
2634 self.assertTrue(str(exc.exception).endswith(msg_suffix))
2635 with self.assertRaises(TypeError) as exc:
2636 @i.register
2637 def _(arg):
2638 return "I forgot to annotate"
2639 self.assertTrue(str(exc.exception).startswith(msg_prefix +
2640 "<function TestSingleDispatch.test_invalid_registrations.<locals>._"
2641 ))
2642 self.assertTrue(str(exc.exception).endswith(msg_suffix))
2643
Łukasz Langae5697532017-12-11 13:56:31 -08002644 with self.assertRaises(TypeError) as exc:
2645 @i.register
2646 def _(arg: typing.Iterable[str]):
2647 # At runtime, dispatching on generics is impossible.
2648 # When registering implementations with singledispatch, avoid
2649 # types from `typing`. Instead, annotate with regular types
2650 # or ABCs.
2651 return "I annotated with a generic collection"
Lysandros Nikolaoud6738102019-05-20 00:11:21 +02002652 self.assertTrue(str(exc.exception).startswith(
2653 "Invalid annotation for 'arg'."
Łukasz Langae5697532017-12-11 13:56:31 -08002654 ))
Lysandros Nikolaoud6738102019-05-20 00:11:21 +02002655 self.assertTrue(str(exc.exception).endswith(
2656 'typing.Iterable[str] is not a class.'
2657 ))
Łukasz Langae5697532017-12-11 13:56:31 -08002658
Dong-hee Na445f1b32018-07-10 16:26:36 +09002659 def test_invalid_positional_argument(self):
2660 @functools.singledispatch
2661 def f(*args):
2662 pass
2663 msg = 'f requires at least 1 positional argument'
INADA Naoki56d8f572018-07-17 13:44:47 +09002664 with self.assertRaisesRegex(TypeError, msg):
Dong-hee Na445f1b32018-07-10 16:26:36 +09002665 f()
Łukasz Langa6f692512013-06-05 12:20:24 +02002666
Carl Meyerd658dea2018-08-28 01:11:56 -06002667
2668class CachedCostItem:
2669 _cost = 1
2670
2671 def __init__(self):
2672 self.lock = py_functools.RLock()
2673
2674 @py_functools.cached_property
2675 def cost(self):
2676 """The cost of the item."""
2677 with self.lock:
2678 self._cost += 1
2679 return self._cost
2680
2681
2682class OptionallyCachedCostItem:
2683 _cost = 1
2684
2685 def get_cost(self):
2686 """The cost of the item."""
2687 self._cost += 1
2688 return self._cost
2689
2690 cached_cost = py_functools.cached_property(get_cost)
2691
2692
2693class CachedCostItemWait:
2694
2695 def __init__(self, event):
2696 self._cost = 1
2697 self.lock = py_functools.RLock()
2698 self.event = event
2699
2700 @py_functools.cached_property
2701 def cost(self):
2702 self.event.wait(1)
2703 with self.lock:
2704 self._cost += 1
2705 return self._cost
2706
2707
2708class CachedCostItemWithSlots:
2709 __slots__ = ('_cost')
2710
2711 def __init__(self):
2712 self._cost = 1
2713
2714 @py_functools.cached_property
2715 def cost(self):
2716 raise RuntimeError('never called, slots not supported')
2717
2718
2719class TestCachedProperty(unittest.TestCase):
2720 def test_cached(self):
2721 item = CachedCostItem()
2722 self.assertEqual(item.cost, 2)
2723 self.assertEqual(item.cost, 2) # not 3
2724
2725 def test_cached_attribute_name_differs_from_func_name(self):
2726 item = OptionallyCachedCostItem()
2727 self.assertEqual(item.get_cost(), 2)
2728 self.assertEqual(item.cached_cost, 3)
2729 self.assertEqual(item.get_cost(), 4)
2730 self.assertEqual(item.cached_cost, 3)
2731
2732 def test_threaded(self):
2733 go = threading.Event()
2734 item = CachedCostItemWait(go)
2735
2736 num_threads = 3
2737
2738 orig_si = sys.getswitchinterval()
2739 sys.setswitchinterval(1e-6)
2740 try:
2741 threads = [
2742 threading.Thread(target=lambda: item.cost)
2743 for k in range(num_threads)
2744 ]
2745 with support.start_threads(threads):
2746 go.set()
2747 finally:
2748 sys.setswitchinterval(orig_si)
2749
2750 self.assertEqual(item.cost, 2)
2751
2752 def test_object_with_slots(self):
2753 item = CachedCostItemWithSlots()
2754 with self.assertRaisesRegex(
2755 TypeError,
2756 "No '__dict__' attribute on 'CachedCostItemWithSlots' instance to cache 'cost' property.",
2757 ):
2758 item.cost
2759
2760 def test_immutable_dict(self):
2761 class MyMeta(type):
2762 @py_functools.cached_property
2763 def prop(self):
2764 return True
2765
2766 class MyClass(metaclass=MyMeta):
2767 pass
2768
2769 with self.assertRaisesRegex(
2770 TypeError,
2771 "The '__dict__' attribute on 'MyMeta' instance does not support item assignment for caching 'prop' property.",
2772 ):
2773 MyClass.prop
2774
2775 def test_reuse_different_names(self):
2776 """Disallow this case because decorated function a would not be cached."""
2777 with self.assertRaises(RuntimeError) as ctx:
2778 class ReusedCachedProperty:
2779 @py_functools.cached_property
2780 def a(self):
2781 pass
2782
2783 b = a
2784
2785 self.assertEqual(
2786 str(ctx.exception.__context__),
2787 str(TypeError("Cannot assign the same cached_property to two different names ('a' and 'b')."))
2788 )
2789
2790 def test_reuse_same_name(self):
2791 """Reusing a cached_property on different classes under the same name is OK."""
2792 counter = 0
2793
2794 @py_functools.cached_property
2795 def _cp(_self):
2796 nonlocal counter
2797 counter += 1
2798 return counter
2799
2800 class A:
2801 cp = _cp
2802
2803 class B:
2804 cp = _cp
2805
2806 a = A()
2807 b = B()
2808
2809 self.assertEqual(a.cp, 1)
2810 self.assertEqual(b.cp, 2)
2811 self.assertEqual(a.cp, 1)
2812
2813 def test_set_name_not_called(self):
2814 cp = py_functools.cached_property(lambda s: None)
2815 class Foo:
2816 pass
2817
2818 Foo.cp = cp
2819
2820 with self.assertRaisesRegex(
2821 TypeError,
2822 "Cannot use cached_property instance without calling __set_name__ on it.",
2823 ):
2824 Foo().cp
2825
2826 def test_access_from_class(self):
2827 self.assertIsInstance(CachedCostItem.cost, py_functools.cached_property)
2828
2829 def test_doc(self):
2830 self.assertEqual(CachedCostItem.cost.__doc__, "The cost of the item.")
2831
2832
Raymond Hettinger9c323f82005-02-28 19:39:44 +00002833if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -05002834 unittest.main()