|
1 | 1 | import unittest |
2 | 2 | import sys |
| 3 | +import contextlib |
3 | 4 | from test.test_support import check_py3k_warnings, CleanImport, run_unittest |
4 | 5 | import warnings |
5 | 6 | import base64 |
@@ -35,6 +36,24 @@ def reset_module_registry(module): |
35 | 36 |
|
36 | 37 | class TestPy3KWarnings(unittest.TestCase): |
37 | 38 |
|
| 39 | + @contextlib.contextmanager |
| 40 | + def check_py3k_warnings_with_fix(self): |
| 41 | + frame = sys._getframe(2) |
| 42 | + registry = frame.f_globals.get('__warningregistry__') |
| 43 | + if registry: |
| 44 | + registry.clear() |
| 45 | + with warnings.catch_warnings(record=True) as w: |
| 46 | + # PyErr_WarnExplicit_WithFix uses this runtime hook directly. |
| 47 | + showwarningwithfix = warnings.showwarningwithfix |
| 48 | + def record_warning_with_fix(*args, **kwargs): |
| 49 | + w.append(warnings.WarningMessageWithFix(*args, **kwargs)) |
| 50 | + warnings.showwarningwithfix = record_warning_with_fix |
| 51 | + warnings.simplefilter("always") |
| 52 | + try: |
| 53 | + yield test_support.WarningsRecorder(w) |
| 54 | + finally: |
| 55 | + warnings.showwarningwithfix = showwarningwithfix |
| 56 | + |
38 | 57 | def assertWarning(self, _, warning, expected_message): |
39 | 58 | self.assertEqual(str(warning.message), expected_message) |
40 | 59 |
|
@@ -424,12 +443,138 @@ def test_b32encode_warns(self): |
424 | 443 | expected = "base64.b32encode returns str in Python 2 (bytes in 3.x)" |
425 | 444 | base64.b32encode(b'test') |
426 | 445 | check_py3k_warnings(expected, UserWarning) |
427 | | - |
| 446 | + |
428 | 447 | def test_b16encode_warns(self): |
429 | 448 | expected = "base64.b16encode returns str in Python 2 (bytes in 3.x)" |
430 | 449 | base64.b16encode(b'test') |
431 | 450 | check_py3k_warnings(expected, UserWarning) |
432 | 451 |
|
| 452 | + def assertMROWarning(self, recorder, expected_message): |
| 453 | + self.assertEqual(len(recorder.warnings), 1) |
| 454 | + msg = str(recorder.warnings[0].message) |
| 455 | + self.assertEqual(msg, expected_message) |
| 456 | + recorder.reset() |
| 457 | + |
| 458 | + def test_classic_mro_resolution_change_warning(self): |
| 459 | + with self.check_py3k_warnings_with_fix() as w: |
| 460 | + class A: |
| 461 | + def do_this(self): |
| 462 | + return "A" |
| 463 | + |
| 464 | + class B(A): |
| 465 | + pass |
| 466 | + |
| 467 | + class C(A): |
| 468 | + def do_this(self): |
| 469 | + return "C" |
| 470 | + |
| 471 | + class D(B, C): |
| 472 | + pass |
| 473 | + |
| 474 | + self.assertEqual(D().do_this(), "A") |
| 475 | + self.assertMROWarning( |
| 476 | + w, |
| 477 | + "classic multiple inheritance for class 'D' will resolve " |
| 478 | + "attribute 'do_this' from 'A' in 2.x but from 'C' in 3.x " |
| 479 | + "due to C3 MRO") |
| 480 | + |
| 481 | + def test_classic_mro_single_inheritance_no_warning(self): |
| 482 | + with self.check_py3k_warnings_with_fix() as w: |
| 483 | + class A: |
| 484 | + def only_here(self): |
| 485 | + return "A" |
| 486 | + |
| 487 | + class B(A): |
| 488 | + pass |
| 489 | + |
| 490 | + self.assertEqual(B().only_here(), "A") |
| 491 | + self.assertEqual(len(w.warnings), 0) |
| 492 | + |
| 493 | + def test_classic_mro_no_conflicting_name_no_warning(self): |
| 494 | + with self.check_py3k_warnings_with_fix() as w: |
| 495 | + class A: |
| 496 | + pass |
| 497 | + |
| 498 | + class B(A): |
| 499 | + def left(self): |
| 500 | + return "left" |
| 501 | + |
| 502 | + class C(A): |
| 503 | + def right(self): |
| 504 | + return "right" |
| 505 | + |
| 506 | + class D(B, C): |
| 507 | + pass |
| 508 | + |
| 509 | + self.assertEqual(D().left(), "left") |
| 510 | + self.assertEqual(D().right(), "right") |
| 511 | + self.assertEqual(len(w.warnings), 0) |
| 512 | + |
| 513 | + def test_classic_mro_provider_unchanged_no_warning(self): |
| 514 | + with self.check_py3k_warnings_with_fix() as w: |
| 515 | + class A: |
| 516 | + def only_here(self): |
| 517 | + return "A" |
| 518 | + |
| 519 | + class B(A): |
| 520 | + pass |
| 521 | + |
| 522 | + class C(A): |
| 523 | + pass |
| 524 | + |
| 525 | + class D(B, C): |
| 526 | + pass |
| 527 | + |
| 528 | + self.assertEqual(D().only_here(), "A") |
| 529 | + self.assertEqual(len(w.warnings), 0) |
| 530 | + |
| 531 | + def test_classic_mro_c3_conflict_warning(self): |
| 532 | + with self.check_py3k_warnings_with_fix() as w: |
| 533 | + class A: |
| 534 | + pass |
| 535 | + |
| 536 | + class B: |
| 537 | + pass |
| 538 | + |
| 539 | + class X(A, B): |
| 540 | + pass |
| 541 | + |
| 542 | + class Y(B, A): |
| 543 | + pass |
| 544 | + |
| 545 | + class Z(X, Y): |
| 546 | + pass |
| 547 | + |
| 548 | + self.assertMROWarning( |
| 549 | + w, |
| 550 | + "classic multiple inheritance hierarchy for class 'Z' has no " |
| 551 | + "consistent C3 MRO and will fail in 3.x") |
| 552 | + |
| 553 | + def test_classic_mro_bases_update_warning(self): |
| 554 | + with self.check_py3k_warnings_with_fix() as w: |
| 555 | + class A: |
| 556 | + def do_this(self): |
| 557 | + return "A" |
| 558 | + |
| 559 | + class B(A): |
| 560 | + pass |
| 561 | + |
| 562 | + class C(A): |
| 563 | + def do_this(self): |
| 564 | + return "C" |
| 565 | + |
| 566 | + class D(B): |
| 567 | + pass |
| 568 | + |
| 569 | + self.assertEqual(len(w.warnings), 0) |
| 570 | + D.__bases__ = (B, C) |
| 571 | + self.assertEqual(D().do_this(), "A") |
| 572 | + self.assertMROWarning( |
| 573 | + w, |
| 574 | + "classic multiple inheritance for class 'D' will resolve " |
| 575 | + "attribute 'do_this' from 'A' in 2.x but from 'C' in 3.x " |
| 576 | + "due to C3 MRO") |
| 577 | + |
433 | 578 |
|
434 | 579 | class TestStdlibRemovals(unittest.TestCase): |
435 | 580 |
|
|
0 commit comments