test_copy.py 20.8 KB
Newer Older
1 2 3
"""Unit tests for the copy module."""

import copy
4
import copyreg
5
import weakref
6
from operator import le, lt, ge, gt, eq, ne
7

8
import unittest
9
from test import support
10

11 12 13 14
order_comparisons = le, lt, ge, gt
equality_comparisons = eq, ne
comparisons = order_comparisons + equality_comparisons

15 16 17 18 19
class TestCopy(unittest.TestCase):

    # Attempt full line coverage of copy.py from top to bottom

    def test_exceptions(self):
20 21
        self.assertTrue(copy.Error is copy.error)
        self.assertTrue(issubclass(copy.Error, Exception))
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

    # The copy() method

    def test_copy_basic(self):
        x = 42
        y = copy.copy(x)
        self.assertEqual(x, y)

    def test_copy_copy(self):
        class C(object):
            def __init__(self, foo):
                self.foo = foo
            def __copy__(self):
                return C(self.foo)
        x = C(42)
        y = copy.copy(x)
        self.assertEqual(y.__class__, x.__class__)
        self.assertEqual(y.foo, x.foo)

41 42 43 44 45 46 47 48 49 50
    def test_copy_registry(self):
        class C(object):
            def __new__(cls, foo):
                obj = object.__new__(cls)
                obj.foo = foo
                return obj
        def pickle_C(obj):
            return (C, (obj.foo,))
        x = C(42)
        self.assertRaises(TypeError, copy.copy, x)
51
        copyreg.pickle(C, pickle_C, C)
52 53
        y = copy.copy(x)

54 55 56 57 58
    def test_copy_reduce_ex(self):
        class C(object):
            def __reduce_ex__(self, proto):
                return ""
            def __reduce__(self):
59
                raise support.TestFailed("shouldn't call this")
60 61
        x = C()
        y = copy.copy(x)
62
        self.assertTrue(y is x)
63

64 65 66 67 68 69
    def test_copy_reduce(self):
        class C(object):
            def __reduce__(self):
                return ""
        x = C()
        y = copy.copy(x)
70
        self.assertTrue(y is x)
71 72

    def test_copy_cant(self):
73
        class C(object):
74
            def __getattribute__(self, name):
75
                if name.startswith("__reduce"):
76
                    raise AttributeError(name)
77 78 79 80 81 82 83 84 85 86 87 88 89
                return object.__getattribute__(self, name)
        x = C()
        self.assertRaises(copy.Error, copy.copy, x)

    # Type-specific _copy_xxx() methods

    def test_copy_atomic(self):
        class Classic:
            pass
        class NewStyle(object):
            pass
        def f():
            pass
90
        tests = [None, 42, 2**100, 3.14, True, False, 1j,
91
                 "hello", "hello\u1234", f.__code__,
92
                 NewStyle, range(10), Classic, max]
93
        for x in tests:
94
            self.assertTrue(copy.copy(x) is x, repr(x))
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

    def test_copy_list(self):
        x = [1, 2, 3]
        self.assertEqual(copy.copy(x), x)

    def test_copy_tuple(self):
        x = (1, 2, 3)
        self.assertEqual(copy.copy(x), x)

    def test_copy_dict(self):
        x = {"foo": 1, "bar": 2}
        self.assertEqual(copy.copy(x), x)

    def test_copy_inst_vanilla(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
112 113
            def __eq__(self, other):
                return self.foo == other.foo
114 115 116 117 118 119 120 121 122
        x = C(42)
        self.assertEqual(copy.copy(x), x)

    def test_copy_inst_copy(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __copy__(self):
                return C(self.foo)
123 124
            def __eq__(self, other):
                return self.foo == other.foo
125 126 127 128 129 130 131 132 133
        x = C(42)
        self.assertEqual(copy.copy(x), x)

    def test_copy_inst_getinitargs(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __getinitargs__(self):
                return (self.foo,)
134 135
            def __eq__(self, other):
                return self.foo == other.foo
136 137 138 139 140 141 142 143 144
        x = C(42)
        self.assertEqual(copy.copy(x), x)

    def test_copy_inst_getstate(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __getstate__(self):
                return {"foo": self.foo}
145 146
            def __eq__(self, other):
                return self.foo == other.foo
147 148 149 150 151 152 153 154 155
        x = C(42)
        self.assertEqual(copy.copy(x), x)

    def test_copy_inst_setstate(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __setstate__(self, state):
                self.foo = state["foo"]
156 157
            def __eq__(self, other):
                return self.foo == other.foo
158 159 160 161 162 163 164 165 166 167 168
        x = C(42)
        self.assertEqual(copy.copy(x), x)

    def test_copy_inst_getstate_setstate(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __getstate__(self):
                return self.foo
            def __setstate__(self, state):
                self.foo = state
169 170
            def __eq__(self, other):
                return self.foo == other.foo
171 172 173 174 175 176 177 178 179 180 181
        x = C(42)
        self.assertEqual(copy.copy(x), x)

    # The deepcopy() method

    def test_deepcopy_basic(self):
        x = 42
        y = copy.deepcopy(x)
        self.assertEqual(y, x)

    def test_deepcopy_memo(self):
182 183
        # Tests of reflexive objects are under type-specific sections below.
        # This tests only repetitions of objects.
184
        x = []
185
        x = [x, x]
186 187
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
188 189 190
        self.assertTrue(y is not x)
        self.assertTrue(y[0] is not x[0])
        self.assertTrue(y[0] is y[1])
191 192 193 194 195 196 197 198

    def test_deepcopy_issubclass(self):
        # XXX Note: there's no way to test the TypeError coming out of
        # issubclass() -- this can only happen when an extension
        # module defines a "type" that doesn't formally inherit from
        # type.
        class Meta(type):
            pass
199 200
        class C(metaclass=Meta):
            pass
201 202 203 204 205 206 207 208 209 210 211 212 213
        self.assertEqual(copy.deepcopy(C), C)

    def test_deepcopy_deepcopy(self):
        class C(object):
            def __init__(self, foo):
                self.foo = foo
            def __deepcopy__(self, memo=None):
                return C(self.foo)
        x = C(42)
        y = copy.deepcopy(x)
        self.assertEqual(y.__class__, x.__class__)
        self.assertEqual(y.foo, x.foo)

214 215 216 217 218 219 220 221 222 223
    def test_deepcopy_registry(self):
        class C(object):
            def __new__(cls, foo):
                obj = object.__new__(cls)
                obj.foo = foo
                return obj
        def pickle_C(obj):
            return (C, (obj.foo,))
        x = C(42)
        self.assertRaises(TypeError, copy.deepcopy, x)
224
        copyreg.pickle(C, pickle_C, C)
225 226
        y = copy.deepcopy(x)

227 228 229 230 231
    def test_deepcopy_reduce_ex(self):
        class C(object):
            def __reduce_ex__(self, proto):
                return ""
            def __reduce__(self):
232
                raise support.TestFailed("shouldn't call this")
233 234
        x = C()
        y = copy.deepcopy(x)
235
        self.assertTrue(y is x)
236

237 238 239 240 241 242
    def test_deepcopy_reduce(self):
        class C(object):
            def __reduce__(self):
                return ""
        x = C()
        y = copy.deepcopy(x)
243
        self.assertTrue(y is x)
244 245

    def test_deepcopy_cant(self):
246
        class C(object):
247
            def __getattribute__(self, name):
248
                if name.startswith("__reduce"):
249
                    raise AttributeError(name)
250 251 252 253 254 255 256 257 258 259 260 261 262
                return object.__getattribute__(self, name)
        x = C()
        self.assertRaises(copy.Error, copy.deepcopy, x)

    # Type-specific _deepcopy_xxx() methods

    def test_deepcopy_atomic(self):
        class Classic:
            pass
        class NewStyle(object):
            pass
        def f():
            pass
263
        tests = [None, 42, 2**100, 3.14, True, False, 1j,
264
                 "hello", "hello\u1234", f.__code__,
265
                 NewStyle, range(10), Classic, max]
266
        for x in tests:
267
            self.assertTrue(copy.deepcopy(x) is x, repr(x))
268 269 270 271 272

    def test_deepcopy_list(self):
        x = [[1, 2], 3]
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
273 274
        self.assertTrue(x is not y)
        self.assertTrue(x[0] is not y[0])
275

276 277 278 279
    def test_deepcopy_reflexive_list(self):
        x = []
        x.append(x)
        y = copy.deepcopy(x)
280 281
        for op in comparisons:
            self.assertRaises(RuntimeError, op, y, x)
282 283
        self.assertTrue(y is not x)
        self.assertTrue(y[0] is y)
284
        self.assertEqual(len(y), 1)
285

286 287 288 289
    def test_deepcopy_tuple(self):
        x = ([1, 2], 3)
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
290 291
        self.assertTrue(x is not y)
        self.assertTrue(x[0] is not y[0])
292

293 294 295 296
    def test_deepcopy_reflexive_tuple(self):
        x = ([],)
        x[0].append(x)
        y = copy.deepcopy(x)
297 298
        for op in comparisons:
            self.assertRaises(RuntimeError, op, y, x)
299 300 301
        self.assertTrue(y is not x)
        self.assertTrue(y[0] is not x[0])
        self.assertTrue(y[0][0] is y)
302

303 304 305 306
    def test_deepcopy_dict(self):
        x = {"foo": [1, 2], "bar": 3}
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
307 308
        self.assertTrue(x is not y)
        self.assertTrue(x["foo"] is not y["foo"])
309

310 311 312 313
    def test_deepcopy_reflexive_dict(self):
        x = {}
        x['foo'] = x
        y = copy.deepcopy(x)
314 315 316 317
        for op in order_comparisons:
            self.assertRaises(TypeError, op, y, x)
        for op in equality_comparisons:
            self.assertRaises(RuntimeError, op, y, x)
318 319
        self.assertTrue(y is not x)
        self.assertTrue(y['foo'] is y)
320
        self.assertEqual(len(y), 1)
321

322 323 324 325
    def test_deepcopy_keepalive(self):
        memo = {}
        x = 42
        y = copy.deepcopy(x, memo)
326
        self.assertTrue(memo[id(x)] is x)
327 328 329 330 331

    def test_deepcopy_inst_vanilla(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
332 333
            def __eq__(self, other):
                return self.foo == other.foo
334 335 336
        x = C([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
337
        self.assertTrue(y.foo is not x.foo)
338 339 340 341 342 343 344

    def test_deepcopy_inst_deepcopy(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __deepcopy__(self, memo):
                return C(copy.deepcopy(self.foo, memo))
345 346
            def __eq__(self, other):
                return self.foo == other.foo
347 348 349
        x = C([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
350 351
        self.assertTrue(y is not x)
        self.assertTrue(y.foo is not x.foo)
352 353 354 355 356 357 358

    def test_deepcopy_inst_getinitargs(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __getinitargs__(self):
                return (self.foo,)
359 360
            def __eq__(self, other):
                return self.foo == other.foo
361 362 363
        x = C([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
364 365
        self.assertTrue(y is not x)
        self.assertTrue(y.foo is not x.foo)
366 367 368 369 370 371 372

    def test_deepcopy_inst_getstate(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __getstate__(self):
                return {"foo": self.foo}
373 374
            def __eq__(self, other):
                return self.foo == other.foo
375 376 377
        x = C([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
378 379
        self.assertTrue(y is not x)
        self.assertTrue(y.foo is not x.foo)
380 381 382 383 384 385 386

    def test_deepcopy_inst_setstate(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __setstate__(self, state):
                self.foo = state["foo"]
387 388
            def __eq__(self, other):
                return self.foo == other.foo
389 390 391
        x = C([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
392 393
        self.assertTrue(y is not x)
        self.assertTrue(y.foo is not x.foo)
394 395 396 397 398 399 400 401 402

    def test_deepcopy_inst_getstate_setstate(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __getstate__(self):
                return self.foo
            def __setstate__(self, state):
                self.foo = state
403 404
            def __eq__(self, other):
                return self.foo == other.foo
405 406 407
        x = C([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
408 409
        self.assertTrue(y is not x)
        self.assertTrue(y.foo is not x.foo)
410

411 412 413 414 415 416
    def test_deepcopy_reflexive_inst(self):
        class C:
            pass
        x = C()
        x.foo = x
        y = copy.deepcopy(x)
417 418
        self.assertTrue(y is not x)
        self.assertTrue(y.foo is y)
419

420 421 422 423 424 425 426 427
    # _reconstruct()

    def test_reconstruct_string(self):
        class C(object):
            def __reduce__(self):
                return ""
        x = C()
        y = copy.copy(x)
428
        self.assertTrue(y is x)
429
        y = copy.deepcopy(x)
430
        self.assertTrue(y is x)
431 432 433 434 435 436 437 438

    def test_reconstruct_nostate(self):
        class C(object):
            def __reduce__(self):
                return (C, ())
        x = C()
        x.foo = 42
        y = copy.copy(x)
439
        self.assertTrue(y.__class__ is x.__class__)
440
        y = copy.deepcopy(x)
441
        self.assertTrue(y.__class__ is x.__class__)
442 443 444 445 446

    def test_reconstruct_state(self):
        class C(object):
            def __reduce__(self):
                return (C, (), self.__dict__)
447 448
            def __eq__(self, other):
                return self.__dict__ == other.__dict__
449 450 451 452 453 454
        x = C()
        x.foo = [42]
        y = copy.copy(x)
        self.assertEqual(y, x)
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
455
        self.assertTrue(y.foo is not x.foo)
456 457 458 459 460 461 462

    def test_reconstruct_state_setstate(self):
        class C(object):
            def __reduce__(self):
                return (C, (), self.__dict__)
            def __setstate__(self, state):
                self.__dict__.update(state)
463 464
            def __eq__(self, other):
                return self.__dict__ == other.__dict__
465 466 467 468 469 470
        x = C()
        x.foo = [42]
        y = copy.copy(x)
        self.assertEqual(y, x)
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
471
        self.assertTrue(y.foo is not x.foo)
472

473 474 475 476 477 478
    def test_reconstruct_reflexive(self):
        class C(object):
            pass
        x = C()
        x.foo = x
        y = copy.deepcopy(x)
479 480
        self.assertTrue(y is not x)
        self.assertTrue(y.foo is y)
481

482 483 484 485 486 487
    # Additions for Python 2.3 and pickle protocol 2

    def test_reduce_4tuple(self):
        class C(list):
            def __reduce__(self):
                return (C, (), self.__dict__, iter(self))
488 489 490
            def __eq__(self, other):
                return (list(self) == list(other) and
                        self.__dict__ == other.__dict__)
491 492 493
        x = C([[1, 2], 3])
        y = copy.copy(x)
        self.assertEqual(x, y)
494 495
        self.assertTrue(x is not y)
        self.assertTrue(x[0] is y[0])
496 497
        y = copy.deepcopy(x)
        self.assertEqual(x, y)
498 499
        self.assertTrue(x is not y)
        self.assertTrue(x[0] is not y[0])
500 501 502 503

    def test_reduce_5tuple(self):
        class C(dict):
            def __reduce__(self):
504
                return (C, (), self.__dict__, None, self.items())
505 506 507
            def __eq__(self, other):
                return (dict(self) == dict(other) and
                        self.__dict__ == other.__dict__)
508 509 510
        x = C([("foo", [1, 2]), ("bar", 3)])
        y = copy.copy(x)
        self.assertEqual(x, y)
511 512
        self.assertTrue(x is not y)
        self.assertTrue(x["foo"] is y["foo"])
513 514
        y = copy.deepcopy(x)
        self.assertEqual(x, y)
515 516
        self.assertTrue(x is not y)
        self.assertTrue(x["foo"] is not y["foo"])
517

518 519 520 521 522 523
    def test_copy_slots(self):
        class C(object):
            __slots__ = ["foo"]
        x = C()
        x.foo = [42]
        y = copy.copy(x)
524
        self.assertTrue(x.foo is y.foo)
525 526 527 528 529 530 531 532

    def test_deepcopy_slots(self):
        class C(object):
            __slots__ = ["foo"]
        x = C()
        x.foo = [42]
        y = copy.deepcopy(x)
        self.assertEqual(x.foo, y.foo)
533
        self.assertTrue(x.foo is not y.foo)
534

535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
    def test_deepcopy_dict_subclass(self):
        class C(dict):
            def __init__(self, d=None):
                if not d:
                    d = {}
                self._keys = list(d.keys())
                super().__init__(d)
            def __setitem__(self, key, item):
                super().__setitem__(key, item)
                if key not in self._keys:
                    self._keys.append(key)
        x = C(d={'foo':0})
        y = copy.deepcopy(x)
        self.assertEqual(x, y)
        self.assertEqual(x._keys, y._keys)
        self.assertTrue(x is not y)
        x['bar'] = 1
        self.assertNotEqual(x, y)
        self.assertNotEqual(x._keys, y._keys)

555 556 557 558 559 560 561 562
    def test_copy_list_subclass(self):
        class C(list):
            pass
        x = C([[1, 2], 3])
        x.foo = [4, 5]
        y = copy.copy(x)
        self.assertEqual(list(x), list(y))
        self.assertEqual(x.foo, y.foo)
563 564
        self.assertTrue(x[0] is y[0])
        self.assertTrue(x.foo is y.foo)
565 566 567 568 569 570 571 572 573

    def test_deepcopy_list_subclass(self):
        class C(list):
            pass
        x = C([[1, 2], 3])
        x.foo = [4, 5]
        y = copy.deepcopy(x)
        self.assertEqual(list(x), list(y))
        self.assertEqual(x.foo, y.foo)
574 575
        self.assertTrue(x[0] is not y[0])
        self.assertTrue(x.foo is not y.foo)
576

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
    def test_copy_tuple_subclass(self):
        class C(tuple):
            pass
        x = C([1, 2, 3])
        self.assertEqual(tuple(x), (1, 2, 3))
        y = copy.copy(x)
        self.assertEqual(tuple(y), (1, 2, 3))

    def test_deepcopy_tuple_subclass(self):
        class C(tuple):
            pass
        x = C([[1, 2], 3])
        self.assertEqual(tuple(x), ([1, 2], 3))
        y = copy.deepcopy(x)
        self.assertEqual(tuple(y), ([1, 2], 3))
592 593
        self.assertTrue(x is not y)
        self.assertTrue(x[0] is not y[0])
594

595 596 597
    def test_getstate_exc(self):
        class EvilState(object):
            def __getstate__(self):
598
                raise ValueError("ain't got no stickin' state")
599 600
        self.assertRaises(ValueError, copy.copy, EvilState())

Guido van Rossum's avatar
Guido van Rossum committed
601 602 603 604 605 606 607 608 609 610 611 612 613 614
    def test_copy_function(self):
        self.assertEqual(copy.copy(global_foo), global_foo)
        def foo(x, y): return x+y
        self.assertEqual(copy.copy(foo), foo)
        bar = lambda: None
        self.assertEqual(copy.copy(bar), bar)

    def test_deepcopy_function(self):
        self.assertEqual(copy.deepcopy(global_foo), global_foo)
        def foo(x, y): return x+y
        self.assertEqual(copy.deepcopy(foo), foo)
        bar = lambda: None
        self.assertEqual(copy.deepcopy(bar), bar)

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
    def _check_weakref(self, _copy):
        class C(object):
            pass
        obj = C()
        x = weakref.ref(obj)
        y = _copy(x)
        self.assertTrue(y is x)
        del obj
        y = _copy(x)
        self.assertTrue(y is x)

    def test_copy_weakref(self):
        self._check_weakref(copy.copy)

    def test_deepcopy_weakref(self):
        self._check_weakref(copy.deepcopy)

    def _check_copy_weakdict(self, _dicttype):
        class C(object):
            pass
        a, b, c, d = [C() for i in range(4)]
        u = _dicttype()
        u[a] = b
        u[c] = d
        v = copy.copy(u)
        self.assertFalse(v is u)
        self.assertEqual(v, u)
        self.assertEqual(v[a], b)
        self.assertEqual(v[c], d)
        self.assertEqual(len(v), 2)
        del c, d
        self.assertEqual(len(v), 1)
        x, y = C(), C()
        # The underlying containers are decoupled
        v[x] = y
        self.assertFalse(x in u)

    def test_copy_weakkeydict(self):
        self._check_copy_weakdict(weakref.WeakKeyDictionary)

    def test_copy_weakvaluedict(self):
        self._check_copy_weakdict(weakref.WeakValueDictionary)

    def test_deepcopy_weakkeydict(self):
        class C(object):
            def __init__(self, i):
                self.i = i
        a, b, c, d = [C(i) for i in range(4)]
        u = weakref.WeakKeyDictionary()
        u[a] = b
        u[c] = d
        # Keys aren't copied, values are
        v = copy.deepcopy(u)
        self.assertNotEqual(v, u)
        self.assertEqual(len(v), 2)
        self.assertFalse(v[a] is b)
        self.assertFalse(v[c] is d)
        self.assertEqual(v[a].i, b.i)
        self.assertEqual(v[c].i, d.i)
        del c
        self.assertEqual(len(v), 1)

    def test_deepcopy_weakvaluedict(self):
        class C(object):
            def __init__(self, i):
                self.i = i
        a, b, c, d = [C(i) for i in range(4)]
        u = weakref.WeakValueDictionary()
        u[a] = b
        u[c] = d
        # Keys are copied, values aren't
        v = copy.deepcopy(u)
        self.assertNotEqual(v, u)
        self.assertEqual(len(v), 2)
        (x, y), (z, t) = sorted(v.items(), key=lambda pair: pair[0].i)
        self.assertFalse(x is a)
        self.assertEqual(x.i, a.i)
        self.assertTrue(y is b)
        self.assertFalse(z is c)
        self.assertEqual(z.i, c.i)
        self.assertTrue(t is d)
        del x, y, z, t
        del d
        self.assertEqual(len(v), 1)


Guido van Rossum's avatar
Guido van Rossum committed
701 702
def global_foo(x, y): return x+y

703
def test_main():
704
    support.run_unittest(TestCopy)
705 706 707

if __name__ == "__main__":
    test_main()