blob: 62d60296bcb5a55f58b9699f68665ce77d724935 [file] [log] [blame]
Serhiy Storchaka71b71762016-02-02 18:45:59 +02001import copy
Nick Coghlanc649ec52006-05-29 12:43:05 +00002import functools
R. David Murrayf28fd242010-02-23 00:24:49 +00003import sys
Raymond Hettinger9c323f82005-02-28 19:39:44 +00004import unittest
5from test import test_support
Raymond Hettingerc8b6d1b2005-03-08 06:14:50 +00006from weakref import proxy
Jack Diederichd60c29e2009-03-31 23:46:48 +00007import pickle
Raymond Hettinger9c323f82005-02-28 19:39:44 +00008
9@staticmethod
10def PythonPartial(func, *args, **keywords):
11 'Pure Python approximation of partial()'
12 def newfunc(*fargs, **fkeywords):
13 newkeywords = keywords.copy()
14 newkeywords.update(fkeywords)
15 return func(*(args + fargs), **newkeywords)
16 newfunc.func = func
17 newfunc.args = args
18 newfunc.keywords = keywords
19 return newfunc
20
21def capture(*args, **kw):
22 """capture all positional and keyword arguments"""
23 return args, kw
24
Jack Diederichd60c29e2009-03-31 23:46:48 +000025def signature(part):
26 """ return the signature of a partial object """
27 return (part.func, part.args, part.keywords, part.__dict__)
28
Serhiy Storchaka71b71762016-02-02 18:45:59 +020029class MyTuple(tuple):
30 pass
31
32class BadTuple(tuple):
33 def __add__(self, other):
34 return list(self) + list(other)
35
36class MyDict(dict):
37 pass
38
Raymond Hettinger9c323f82005-02-28 19:39:44 +000039class TestPartial(unittest.TestCase):
40
Serhiy Storchaka59f71142016-06-12 15:43:57 +030041 partial = functools.partial
Raymond Hettinger9c323f82005-02-28 19:39:44 +000042
43 def test_basic_examples(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +030044 p = self.partial(capture, 1, 2, a=10, b=20)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000045 self.assertEqual(p(3, 4, b=30, c=40),
46 ((1, 2, 3, 4), dict(a=10, b=30, c=40)))
Serhiy Storchaka59f71142016-06-12 15:43:57 +030047 p = self.partial(map, lambda x: x*10)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000048 self.assertEqual(p([1,2,3,4]), [10, 20, 30, 40])
49
50 def test_attributes(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +030051 p = self.partial(capture, 1, 2, a=10, b=20)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000052 # attributes should be readable
53 self.assertEqual(p.func, capture)
54 self.assertEqual(p.args, (1, 2))
55 self.assertEqual(p.keywords, dict(a=10, b=20))
56 # attributes should not be writable
Raymond Hettinger9c323f82005-02-28 19:39:44 +000057 self.assertRaises(TypeError, setattr, p, 'func', map)
58 self.assertRaises(TypeError, setattr, p, 'args', (1, 2))
59 self.assertRaises(TypeError, setattr, p, 'keywords', dict(a=1, b=2))
60
Serhiy Storchaka59f71142016-06-12 15:43:57 +030061 p = self.partial(hex)
Georg Brandla34f87f2010-02-07 12:27:06 +000062 try:
63 del p.__dict__
64 except TypeError:
65 pass
66 else:
67 self.fail('partial object allowed __dict__ to be deleted')
68
Raymond Hettinger9c323f82005-02-28 19:39:44 +000069 def test_argument_checking(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +030070 self.assertRaises(TypeError, self.partial) # need at least a func arg
Raymond Hettinger9c323f82005-02-28 19:39:44 +000071 try:
Serhiy Storchaka59f71142016-06-12 15:43:57 +030072 self.partial(2)()
Raymond Hettinger9c323f82005-02-28 19:39:44 +000073 except TypeError:
74 pass
75 else:
76 self.fail('First arg not checked for callability')
77
78 def test_protection_of_callers_dict_argument(self):
79 # a caller's dictionary should not be altered by partial
80 def func(a=10, b=20):
81 return a
82 d = {'a':3}
Serhiy Storchaka59f71142016-06-12 15:43:57 +030083 p = self.partial(func, a=5)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000084 self.assertEqual(p(**d), 3)
85 self.assertEqual(d, {'a':3})
86 p(b=7)
87 self.assertEqual(d, {'a':3})
88
89 def test_arg_combinations(self):
90 # exercise special code paths for zero args in either partial
91 # object or the caller
Serhiy Storchaka59f71142016-06-12 15:43:57 +030092 p = self.partial(capture)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000093 self.assertEqual(p(), ((), {}))
94 self.assertEqual(p(1,2), ((1,2), {}))
Serhiy Storchaka59f71142016-06-12 15:43:57 +030095 p = self.partial(capture, 1, 2)
Raymond Hettinger9c323f82005-02-28 19:39:44 +000096 self.assertEqual(p(), ((1,2), {}))
97 self.assertEqual(p(3,4), ((1,2,3,4), {}))
98
99 def test_kw_combinations(self):
100 # exercise special code paths for no keyword args in
101 # either the partial object or the caller
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300102 p = self.partial(capture)
Benjamin Peterson72c01412015-05-09 00:23:41 -0400103 self.assertEqual(p.keywords, {})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000104 self.assertEqual(p(), ((), {}))
105 self.assertEqual(p(a=1), ((), {'a':1}))
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300106 p = self.partial(capture, a=1)
Benjamin Peterson72c01412015-05-09 00:23:41 -0400107 self.assertEqual(p.keywords, {'a':1})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000108 self.assertEqual(p(), ((), {'a':1}))
109 self.assertEqual(p(b=2), ((), {'a':1, 'b':2}))
110 # keyword args in the call override those in the partial object
111 self.assertEqual(p(a=3, b=2), ((), {'a':3, 'b':2}))
112
113 def test_positional(self):
114 # make sure positional arguments are captured correctly
115 for args in [(), (0,), (0,1), (0,1,2), (0,1,2,3)]:
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300116 p = self.partial(capture, *args)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000117 expected = args + ('x',)
118 got, empty = p('x')
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000119 self.assertTrue(expected == got and empty == {})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000120
121 def test_keyword(self):
122 # make sure keyword arguments are captured correctly
123 for a in ['a', 0, None, 3.5]:
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300124 p = self.partial(capture, a=a)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000125 expected = {'a':a,'x':None}
126 empty, got = p(x=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000127 self.assertTrue(expected == got and empty == ())
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000128
129 def test_no_side_effects(self):
130 # make sure there are no side effects that affect subsequent calls
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300131 p = self.partial(capture, 0, a=1)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000132 args1, kw1 = p(1, b=2)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000133 self.assertTrue(args1 == (0,1) and kw1 == {'a':1,'b':2})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000134 args2, kw2 = p()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000135 self.assertTrue(args2 == (0,) and kw2 == {'a':1})
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000136
137 def test_error_propagation(self):
138 def f(x, y):
Ezio Melottidde5b942010-02-03 05:37:26 +0000139 x // y
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300140 self.assertRaises(ZeroDivisionError, self.partial(f, 1, 0))
141 self.assertRaises(ZeroDivisionError, self.partial(f, 1), 0)
142 self.assertRaises(ZeroDivisionError, self.partial(f), 1, 0)
143 self.assertRaises(ZeroDivisionError, self.partial(f, y=0), 1)
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000144
Raymond Hettingerc8b6d1b2005-03-08 06:14:50 +0000145 def test_weakref(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300146 f = self.partial(int, base=16)
Raymond Hettingerc8b6d1b2005-03-08 06:14:50 +0000147 p = proxy(f)
148 self.assertEqual(f.func, p.func)
149 f = None
150 self.assertRaises(ReferenceError, getattr, p, 'func')
151
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000152 def test_with_bound_and_unbound_methods(self):
153 data = map(str, range(10))
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300154 join = self.partial(str.join, '')
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000155 self.assertEqual(join(data), '0123456789')
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300156 join = self.partial(''.join)
Raymond Hettinger26e512a2005-03-11 06:48:49 +0000157 self.assertEqual(join(data), '0123456789')
Tim Peterseba28be2005-03-28 01:08:02 +0000158
Jack Diederichd60c29e2009-03-31 23:46:48 +0000159 def test_pickle(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300160 f = self.partial(signature, ['asdf'], bar=[True])
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200161 f.attr = []
Serhiy Storchaka655720e2014-12-15 14:02:43 +0200162 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
163 f_copy = pickle.loads(pickle.dumps(f, proto))
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200164 self.assertEqual(signature(f_copy), signature(f))
165
166 def test_copy(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300167 f = self.partial(signature, ['asdf'], bar=[True])
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200168 f.attr = []
169 f_copy = copy.copy(f)
170 self.assertEqual(signature(f_copy), signature(f))
171 self.assertIs(f_copy.attr, f.attr)
172 self.assertIs(f_copy.args, f.args)
173 self.assertIs(f_copy.keywords, f.keywords)
174
175 def test_deepcopy(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300176 f = self.partial(signature, ['asdf'], bar=[True])
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200177 f.attr = []
178 f_copy = copy.deepcopy(f)
179 self.assertEqual(signature(f_copy), signature(f))
180 self.assertIsNot(f_copy.attr, f.attr)
181 self.assertIsNot(f_copy.args, f.args)
182 self.assertIsNot(f_copy.args[0], f.args[0])
183 self.assertIsNot(f_copy.keywords, f.keywords)
184 self.assertIsNot(f_copy.keywords['bar'], f.keywords['bar'])
185
186 def test_setstate(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300187 f = self.partial(signature)
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200188 f.__setstate__((capture, (1,), dict(a=10), dict(attr=[])))
189 self.assertEqual(signature(f),
190 (capture, (1,), dict(a=10), dict(attr=[])))
191 self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20}))
192
193 f.__setstate__((capture, (1,), dict(a=10), None))
194 self.assertEqual(signature(f), (capture, (1,), dict(a=10), {}))
195 self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20}))
196
197 f.__setstate__((capture, (1,), None, None))
198 #self.assertEqual(signature(f), (capture, (1,), {}, {}))
199 self.assertEqual(f(2, b=20), ((1, 2), {'b': 20}))
200 self.assertEqual(f(2), ((1, 2), {}))
201 self.assertEqual(f(), ((1,), {}))
202
203 f.__setstate__((capture, (), {}, None))
204 self.assertEqual(signature(f), (capture, (), {}, {}))
205 self.assertEqual(f(2, b=20), ((2,), {'b': 20}))
206 self.assertEqual(f(2), ((2,), {}))
207 self.assertEqual(f(), ((), {}))
208
209 def test_setstate_errors(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300210 f = self.partial(signature)
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200211 self.assertRaises(TypeError, f.__setstate__, (capture, (), {}))
212 self.assertRaises(TypeError, f.__setstate__, (capture, (), {}, {}, None))
213 self.assertRaises(TypeError, f.__setstate__, [capture, (), {}, None])
214 self.assertRaises(TypeError, f.__setstate__, (None, (), {}, None))
215 self.assertRaises(TypeError, f.__setstate__, (capture, None, {}, None))
216 self.assertRaises(TypeError, f.__setstate__, (capture, [], {}, None))
217 self.assertRaises(TypeError, f.__setstate__, (capture, (), [], None))
218
219 def test_setstate_subclasses(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300220 f = self.partial(signature)
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200221 f.__setstate__((capture, MyTuple((1,)), MyDict(a=10), None))
222 s = signature(f)
223 self.assertEqual(s, (capture, (1,), dict(a=10), {}))
224 self.assertIs(type(s[1]), tuple)
225 self.assertIs(type(s[2]), dict)
226 r = f()
227 self.assertEqual(r, ((1,), {'a': 10}))
228 self.assertIs(type(r[0]), tuple)
229 self.assertIs(type(r[1]), dict)
230
231 f.__setstate__((capture, BadTuple((1,)), {}, None))
232 s = signature(f)
233 self.assertEqual(s, (capture, (1,), {}, {}))
234 self.assertIs(type(s[1]), tuple)
235 r = f(2)
236 self.assertEqual(r, ((1, 2), {}))
237 self.assertIs(type(r[0]), tuple)
Jack Diederichd60c29e2009-03-31 23:46:48 +0000238
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300239 def test_recursive_pickle(self):
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300240 f = self.partial(capture)
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300241 f.__setstate__((f, (), {}, {}))
Serhiy Storchaka50c7e052016-06-12 15:45:14 +0300242 try:
243 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
244 with self.assertRaises(RuntimeError):
245 pickle.dumps(f, proto)
246 finally:
247 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300248
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300249 f = self.partial(capture)
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300250 f.__setstate__((capture, (f,), {}, {}))
Serhiy Storchaka50c7e052016-06-12 15:45:14 +0300251 try:
252 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
253 f_copy = pickle.loads(pickle.dumps(f, proto))
254 try:
255 self.assertIs(f_copy.args[0], f_copy)
256 finally:
257 f_copy.__setstate__((capture, (), {}, {}))
258 finally:
259 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300260
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300261 f = self.partial(capture)
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300262 f.__setstate__((capture, (), {'a': f}, {}))
Serhiy Storchaka50c7e052016-06-12 15:45:14 +0300263 try:
264 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
265 f_copy = pickle.loads(pickle.dumps(f, proto))
266 try:
267 self.assertIs(f_copy.keywords['a'], f_copy)
268 finally:
269 f_copy.__setstate__((capture, (), {}, {}))
270 finally:
271 f.__setstate__((capture, (), {}, {}))
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300272
Serhiy Storchakaa07a8b42013-02-04 12:45:46 +0200273 # Issue 6083: Reference counting bug
274 def test_setstate_refcount(self):
275 class BadSequence:
276 def __len__(self):
277 return 4
278 def __getitem__(self, key):
279 if key == 0:
280 return max
281 elif key == 1:
282 return tuple(range(1000000))
283 elif key in (2, 3):
284 return {}
285 raise IndexError
286
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300287 f = self.partial(object)
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200288 self.assertRaises(TypeError, f.__setstate__, BadSequence())
Serhiy Storchakaa07a8b42013-02-04 12:45:46 +0200289
Nick Coghlanc649ec52006-05-29 12:43:05 +0000290class PartialSubclass(functools.partial):
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000291 pass
292
293class TestPartialSubclass(TestPartial):
294
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300295 partial = PartialSubclass
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000296
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000297class TestPythonPartial(TestPartial):
298
Serhiy Storchaka59f71142016-06-12 15:43:57 +0300299 partial = PythonPartial
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000300
Jack Diederichd60c29e2009-03-31 23:46:48 +0000301 # the python version isn't picklable
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200302 test_pickle = None
303 test_setstate = None
304 test_setstate_errors = None
305 test_setstate_subclasses = None
306 test_setstate_refcount = None
Serhiy Storchaka1e090062016-06-12 15:08:57 +0300307 test_recursive_pickle = None
Serhiy Storchaka71b71762016-02-02 18:45:59 +0200308
309 # the python version isn't deepcopyable
310 test_deepcopy = None
Zachary Ware1f702212013-12-10 14:09:20 -0600311
312 # the python version isn't a type
313 test_attributes = None
Jack Diederichd60c29e2009-03-31 23:46:48 +0000314
Nick Coghlan676725d2006-06-08 13:54:49 +0000315class TestUpdateWrapper(unittest.TestCase):
316
317 def check_wrapper(self, wrapper, wrapped,
318 assigned=functools.WRAPPER_ASSIGNMENTS,
319 updated=functools.WRAPPER_UPDATES):
320 # Check attributes were assigned
321 for name in assigned:
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000322 self.assertTrue(getattr(wrapper, name) is getattr(wrapped, name))
Nick Coghlan676725d2006-06-08 13:54:49 +0000323 # Check attributes were updated
324 for name in updated:
325 wrapper_attr = getattr(wrapper, name)
326 wrapped_attr = getattr(wrapped, name)
327 for key in wrapped_attr:
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000328 self.assertTrue(wrapped_attr[key] is wrapper_attr[key])
Nick Coghlan676725d2006-06-08 13:54:49 +0000329
R. David Murrayf28fd242010-02-23 00:24:49 +0000330 def _default_update(self):
Nick Coghlan676725d2006-06-08 13:54:49 +0000331 def f():
332 """This is a test"""
333 pass
334 f.attr = 'This is also a test'
335 def wrapper():
336 pass
337 functools.update_wrapper(wrapper, f)
R. David Murrayf28fd242010-02-23 00:24:49 +0000338 return wrapper, f
339
340 def test_default_update(self):
341 wrapper, f = self._default_update()
Nick Coghlan676725d2006-06-08 13:54:49 +0000342 self.check_wrapper(wrapper, f)
343 self.assertEqual(wrapper.__name__, 'f')
Nick Coghlan676725d2006-06-08 13:54:49 +0000344 self.assertEqual(wrapper.attr, 'This is also a test')
345
R. David Murrayf28fd242010-02-23 00:24:49 +0000346 @unittest.skipIf(sys.flags.optimize >= 2,
347 "Docstrings are omitted with -O2 and above")
348 def test_default_update_doc(self):
349 wrapper, f = self._default_update()
350 self.assertEqual(wrapper.__doc__, 'This is a test')
351
Nick Coghlan676725d2006-06-08 13:54:49 +0000352 def test_no_update(self):
353 def f():
354 """This is a test"""
355 pass
356 f.attr = 'This is also a test'
357 def wrapper():
358 pass
359 functools.update_wrapper(wrapper, f, (), ())
360 self.check_wrapper(wrapper, f, (), ())
361 self.assertEqual(wrapper.__name__, 'wrapper')
362 self.assertEqual(wrapper.__doc__, None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000363 self.assertFalse(hasattr(wrapper, 'attr'))
Nick Coghlan676725d2006-06-08 13:54:49 +0000364
365 def test_selective_update(self):
366 def f():
367 pass
368 f.attr = 'This is a different test'
369 f.dict_attr = dict(a=1, b=2, c=3)
370 def wrapper():
371 pass
372 wrapper.dict_attr = {}
373 assign = ('attr',)
374 update = ('dict_attr',)
375 functools.update_wrapper(wrapper, f, assign, update)
376 self.check_wrapper(wrapper, f, assign, update)
377 self.assertEqual(wrapper.__name__, 'wrapper')
378 self.assertEqual(wrapper.__doc__, None)
379 self.assertEqual(wrapper.attr, 'This is a different test')
380 self.assertEqual(wrapper.dict_attr, f.dict_attr)
381
Serhiy Storchaka72121c62013-01-27 19:45:49 +0200382 @test_support.requires_docstrings
Andrew M. Kuchling41eb7162006-10-27 16:39:10 +0000383 def test_builtin_update(self):
384 # Test for bug #1576241
385 def wrapper():
386 pass
387 functools.update_wrapper(wrapper, max)
388 self.assertEqual(wrapper.__name__, 'max')
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000389 self.assertTrue(wrapper.__doc__.startswith('max('))
Nick Coghlan676725d2006-06-08 13:54:49 +0000390
391class TestWraps(TestUpdateWrapper):
392
R. David Murrayf28fd242010-02-23 00:24:49 +0000393 def _default_update(self):
Nick Coghlan676725d2006-06-08 13:54:49 +0000394 def f():
395 """This is a test"""
396 pass
397 f.attr = 'This is also a test'
398 @functools.wraps(f)
399 def wrapper():
400 pass
401 self.check_wrapper(wrapper, f)
R. David Murrayf28fd242010-02-23 00:24:49 +0000402 return wrapper
403
404 def test_default_update(self):
405 wrapper = self._default_update()
Nick Coghlan676725d2006-06-08 13:54:49 +0000406 self.assertEqual(wrapper.__name__, 'f')
Nick Coghlan676725d2006-06-08 13:54:49 +0000407 self.assertEqual(wrapper.attr, 'This is also a test')
408
Serhiy Storchaka80a0a1e2013-01-28 13:24:01 +0200409 @unittest.skipIf(sys.flags.optimize >= 2,
R. David Murrayf28fd242010-02-23 00:24:49 +0000410 "Docstrings are omitted with -O2 and above")
411 def test_default_update_doc(self):
412 wrapper = self._default_update()
413 self.assertEqual(wrapper.__doc__, 'This is a test')
414
Nick Coghlan676725d2006-06-08 13:54:49 +0000415 def test_no_update(self):
416 def f():
417 """This is a test"""
418 pass
419 f.attr = 'This is also a test'
420 @functools.wraps(f, (), ())
421 def wrapper():
422 pass
423 self.check_wrapper(wrapper, f, (), ())
424 self.assertEqual(wrapper.__name__, 'wrapper')
425 self.assertEqual(wrapper.__doc__, None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000426 self.assertFalse(hasattr(wrapper, 'attr'))
Nick Coghlan676725d2006-06-08 13:54:49 +0000427
428 def test_selective_update(self):
429 def f():
430 pass
431 f.attr = 'This is a different test'
432 f.dict_attr = dict(a=1, b=2, c=3)
433 def add_dict_attr(f):
434 f.dict_attr = {}
435 return f
436 assign = ('attr',)
437 update = ('dict_attr',)
438 @functools.wraps(f, assign, update)
439 @add_dict_attr
440 def wrapper():
441 pass
442 self.check_wrapper(wrapper, f, assign, update)
443 self.assertEqual(wrapper.__name__, 'wrapper')
444 self.assertEqual(wrapper.__doc__, None)
445 self.assertEqual(wrapper.attr, 'This is a different test')
446 self.assertEqual(wrapper.dict_attr, f.dict_attr)
447
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000448
Brett Cannon83e81842008-08-09 23:30:55 +0000449class TestReduce(unittest.TestCase):
450
451 def test_reduce(self):
452 class Squares:
453
454 def __init__(self, max):
455 self.max = max
456 self.sofar = []
457
458 def __len__(self): return len(self.sofar)
459
460 def __getitem__(self, i):
461 if not 0 <= i < self.max: raise IndexError
462 n = len(self.sofar)
463 while n <= i:
464 self.sofar.append(n*n)
465 n += 1
466 return self.sofar[i]
467
468 reduce = functools.reduce
469 self.assertEqual(reduce(lambda x, y: x+y, ['a', 'b', 'c'], ''), 'abc')
470 self.assertEqual(
471 reduce(lambda x, y: x+y, [['a', 'c'], [], ['d', 'w']], []),
472 ['a','c','d','w']
473 )
474 self.assertEqual(reduce(lambda x, y: x*y, range(2,8), 1), 5040)
475 self.assertEqual(
476 reduce(lambda x, y: x*y, range(2,21), 1L),
477 2432902008176640000L
478 )
479 self.assertEqual(reduce(lambda x, y: x+y, Squares(10)), 285)
480 self.assertEqual(reduce(lambda x, y: x+y, Squares(10), 0), 285)
481 self.assertEqual(reduce(lambda x, y: x+y, Squares(0), 0), 0)
482 self.assertRaises(TypeError, reduce)
483 self.assertRaises(TypeError, reduce, 42, 42)
484 self.assertRaises(TypeError, reduce, 42, 42, 42)
485 self.assertEqual(reduce(42, "1"), "1") # func is never called with one item
486 self.assertEqual(reduce(42, "", "1"), "1") # func is never called with one item
487 self.assertRaises(TypeError, reduce, 42, (42, 42))
488
Raymond Hettingerbb006cf2010-04-04 21:45:01 +0000489class TestCmpToKey(unittest.TestCase):
490 def test_cmp_to_key(self):
491 def mycmp(x, y):
492 return y - x
493 self.assertEqual(sorted(range(5), key=functools.cmp_to_key(mycmp)),
494 [4, 3, 2, 1, 0])
Brett Cannon83e81842008-08-09 23:30:55 +0000495
Raymond Hettingere1d665a2010-04-05 18:53:43 +0000496 def test_hash(self):
497 def mycmp(x, y):
498 return y - x
499 key = functools.cmp_to_key(mycmp)
500 k = key(10)
501 self.assertRaises(TypeError, hash(k))
502
Raymond Hettinger06bc0b62010-04-04 22:24:03 +0000503class TestTotalOrdering(unittest.TestCase):
504
505 def test_total_ordering_lt(self):
506 @functools.total_ordering
507 class A:
508 def __init__(self, value):
509 self.value = value
510 def __lt__(self, other):
511 return self.value < other.value
Éric Araujo374274d2011-03-19 04:29:36 +0100512 def __eq__(self, other):
513 return self.value == other.value
Ezio Melotti2623a372010-11-21 13:34:58 +0000514 self.assertTrue(A(1) < A(2))
515 self.assertTrue(A(2) > A(1))
516 self.assertTrue(A(1) <= A(2))
517 self.assertTrue(A(2) >= A(1))
518 self.assertTrue(A(2) <= A(2))
519 self.assertTrue(A(2) >= A(2))
Raymond Hettinger06bc0b62010-04-04 22:24:03 +0000520
521 def test_total_ordering_le(self):
522 @functools.total_ordering
523 class A:
524 def __init__(self, value):
525 self.value = value
526 def __le__(self, other):
527 return self.value <= other.value
Éric Araujo374274d2011-03-19 04:29:36 +0100528 def __eq__(self, other):
529 return self.value == other.value
Ezio Melotti2623a372010-11-21 13:34:58 +0000530 self.assertTrue(A(1) < A(2))
531 self.assertTrue(A(2) > A(1))
532 self.assertTrue(A(1) <= A(2))
533 self.assertTrue(A(2) >= A(1))
534 self.assertTrue(A(2) <= A(2))
535 self.assertTrue(A(2) >= A(2))
Raymond Hettinger06bc0b62010-04-04 22:24:03 +0000536
537 def test_total_ordering_gt(self):
538 @functools.total_ordering
539 class A:
540 def __init__(self, value):
541 self.value = value
542 def __gt__(self, other):
543 return self.value > other.value
Éric Araujo374274d2011-03-19 04:29:36 +0100544 def __eq__(self, other):
545 return self.value == other.value
Ezio Melotti2623a372010-11-21 13:34:58 +0000546 self.assertTrue(A(1) < A(2))
547 self.assertTrue(A(2) > A(1))
548 self.assertTrue(A(1) <= A(2))
549 self.assertTrue(A(2) >= A(1))
550 self.assertTrue(A(2) <= A(2))
551 self.assertTrue(A(2) >= A(2))
Raymond Hettinger06bc0b62010-04-04 22:24:03 +0000552
553 def test_total_ordering_ge(self):
554 @functools.total_ordering
555 class A:
556 def __init__(self, value):
557 self.value = value
558 def __ge__(self, other):
559 return self.value >= other.value
Éric Araujo374274d2011-03-19 04:29:36 +0100560 def __eq__(self, other):
561 return self.value == other.value
Ezio Melotti2623a372010-11-21 13:34:58 +0000562 self.assertTrue(A(1) < A(2))
563 self.assertTrue(A(2) > A(1))
564 self.assertTrue(A(1) <= A(2))
565 self.assertTrue(A(2) >= A(1))
566 self.assertTrue(A(2) <= A(2))
567 self.assertTrue(A(2) >= A(2))
Raymond Hettinger06bc0b62010-04-04 22:24:03 +0000568
569 def test_total_ordering_no_overwrite(self):
570 # new methods should not overwrite existing
571 @functools.total_ordering
Benjamin Peterson9d0eaac2010-08-23 17:45:31 +0000572 class A(str):
Raymond Hettinger06bc0b62010-04-04 22:24:03 +0000573 pass
Ezio Melotti2623a372010-11-21 13:34:58 +0000574 self.assertTrue(A("a") < A("b"))
575 self.assertTrue(A("b") > A("a"))
576 self.assertTrue(A("a") <= A("b"))
577 self.assertTrue(A("b") >= A("a"))
578 self.assertTrue(A("b") <= A("b"))
579 self.assertTrue(A("b") >= A("b"))
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000580
Benjamin Petersona11da592010-04-11 01:40:32 +0000581 def test_no_operations_defined(self):
582 with self.assertRaises(ValueError):
583 @functools.total_ordering
584 class A:
585 pass
586
Éric Araujo374274d2011-03-19 04:29:36 +0100587 def test_bug_10042(self):
588 @functools.total_ordering
589 class TestTO:
590 def __init__(self, value):
591 self.value = value
592 def __eq__(self, other):
593 if isinstance(other, TestTO):
594 return self.value == other.value
595 return False
596 def __lt__(self, other):
597 if isinstance(other, TestTO):
598 return self.value < other.value
599 raise TypeError
600 with self.assertRaises(TypeError):
601 TestTO(8) <= ()
602
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000603def test_main(verbose=None):
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000604 test_classes = (
605 TestPartial,
606 TestPartialSubclass,
607 TestPythonPartial,
Nick Coghlan676725d2006-06-08 13:54:49 +0000608 TestUpdateWrapper,
Benjamin Peterson9d0eaac2010-08-23 17:45:31 +0000609 TestTotalOrdering,
Brett Cannon83e81842008-08-09 23:30:55 +0000610 TestWraps,
611 TestReduce,
Raymond Hettinger9c323f82005-02-28 19:39:44 +0000612 )
613 test_support.run_unittest(*test_classes)
614
615 # verify reference counting
616 if verbose and hasattr(sys, "gettotalrefcount"):
617 import gc
618 counts = [None] * 5
619 for i in xrange(len(counts)):
620 test_support.run_unittest(*test_classes)
621 gc.collect()
622 counts[i] = sys.gettotalrefcount()
623 print counts
624
625if __name__ == '__main__':
626 test_main(verbose=True)